Odzwierciedlenie nazwy parametru: nadużycie wyrażeń C # lambda lub błyskotliwość składni?

428

Patrzę na składnik MvcContrib Grid i jestem zafascynowany, ale jednocześnie odparty, przez sztuczkę składniową zastosowaną w składni Grid :

.Attributes(style => "width:100%")

Powyższa składnia ustawia atrybut stylu wygenerowanego HTML na width:100%. Teraz, jeśli zwrócisz uwagę, „styl” nigdzie nie jest określony, wywnioskuje się z nazwy parametru w wyrażeniu! Musiałem się w to zagłębić i odkryłem, gdzie dzieje się „magia”:

Hash(params Func<object, TValue>[] hash)
{
    foreach (var func in hash)
    {
        Add(func.Method.GetParameters()[0].Name, func(null));
    }
}

W rzeczy samej, kod używa formalnej, czas kompilacji, nazwy parametrów do stworzenia słownika par nazwa-wartość atrybutu. Wynikowa konstrukcja składni jest rzeczywiście bardzo ekspresyjna, ale jednocześnie bardzo niebezpieczna. Ogólne użycie wyrażeń lambda pozwala na zastąpienie używanych nazw bez skutków ubocznych. Widzę przykład w książce, który mówi, collection.ForEach(book => Fire.Burn(book))że wiem, że mogę pisać w moim kodzie, collection.ForEach(log => Fire.Burn(log))a to oznacza to samo . Ale dzięki składni MvcContrib Grid nagle znajduję kod, który aktywnie wyszukuje i podejmuje decyzje na podstawie nazw wybranych dla moich zmiennych!

Czy więc jest to powszechna praktyka w społeczności C # 3.5 / 4.0 i miłośnikach wyrażeń lambda? A może jest nieuczciwym indywidualistą, którego nie powinienem się martwić?

Remus Rusanu
źródło
34
Argumentowałbym, że wygląda to na oczywiste, o ile jesteś skłonny przyjrzeć się intencji kodu, a nie tylko analizować składnię. Co, jeśli czytasz dobry kod, i tak powinieneś robić. Składnia jest tylko narzędziem intencji i argumentowałbym, że jest to kod ujawniający intencję.
Jamie Penney,
190
Zapytałem tylko Andersa (i resztę zespołu projektantów), co myślą. Powiedzmy, że wyników nie da się wydrukować w gazecie przyjaznej rodzinie.
Eric Lippert,
22
W C # brakuje obecnie czystej, lekkiej składni map, zwłaszcza map przekazywanych do funkcji. Różne języki dynamiczne (Ruby, Python, JavaScript, ColdFusion 9) mają do tego czystą, lekką składnię, do pewnego stopnia.
yfeldblum
28
Och, myślę, że WYGLĄDA zachwycająco. Jeśli chodzi o składnię map, tak, byłoby świetnie, gdybyśmy mogli zrobić nowe {{„Do”, „A deer”}, {„Re”, „golden sun”} ...} i pozwolić, aby kompilator wyprowadził konstrukcja mapy, podobnie jak nowa [] {1, 2,3} podaje konstrukcję tablicy int. Rozważamy tego rodzaju rzeczy.
Eric Lippert,
17
Eric Lippert, bardzo cię szanuję, ale myślę, że ty i cała grupa (w tym Anders, których szanuję PRAWDZIWIE WOLNO), wtrącasz to zbyt surowo. Jak przyznajesz, C # nie ma ścisłej składni map, a niektóre inne języki (jak Ruby) mają świetne. Ten facet znalazł sposób na uzyskanie składni, której chciał. Przyznaję, że istnieją podobne sposoby wyrażania tego, które są prawie tak wyraziste, jak jego składnia z mniejszą liczbą wad. Ale jego składnia i fakt, że tak ciężko pracował, aby ją uzyskać, wyraźnie wskazują na potrzebę ulepszenia języka. Wyniki osiągnięte w przeszłości wskazują, że stworzysz dla tego coś wspaniałego.
Charlie Flowers

Odpowiedzi:

147

To ma słabą interop. Weźmy na przykład ten przykład w C # - F #

DO#:

public class Class1
{
    public static void Foo(Func<object, string> f)
    {
        Console.WriteLine(f.Method.GetParameters()[0].Name);
    }
}

FA#:

Class1.Foo(fun yadda -> "hello")

Wynik:

Drukowane jest „arg” (nie „yadda”).

W rezultacie projektanci bibliotek powinni albo unikać tego rodzaju „nadużyć”, albo przynajmniej zapewniać „standardowe” przeciążenie (np. Traktuje nazwę łańcucha jako dodatkowy parametr), jeśli chcą mieć dobrą współpracę między językami .Net.

Brian
źródło
11
Ty nie. Ta strategia jest po prostu nieprzenośna. (W przypadku, gdy pomaga, na przykład w inny sposób, F # może być w stanie przeciążać metody, które różnią się tylko typem zwracanym (wnioskowanie typu może to zrobić). Można to wyrazić w CLR. Ale F # nie zezwala na to, głównie dlatego, że jeśli zrobiłeś to, te interfejsy API nie będą mogły być wywoływane z C #.) Jeśli chodzi o interop, zawsze występują kompromisy w „krawędziowych” funkcjach dotyczących korzyści, jakie otrzymujesz w porównaniu z interakcją, którą wymieniasz.
Brian
32
Nie lubię braku interoperacyjności jako powodu, aby coś nie robić. Jeśli interoperacyjność jest wymogiem, zrób to, jeśli nie, to po co się tym martwić? To jest YAGNI IMHO.
John Farrell,
15
@jfar: W .NET CLR interoperacyjność lądowa ma zupełnie nowy wymiar, asemblery generowane w dowolnym kompilatorze powinny być pobierane z dowolnego innego języka.
Remus Rusanu,
25
Zgadzam się, że nie musisz być zgodny z CLS, ale wydaje się dobrym pomysłem, jeśli piszesz bibliotekę lub kontrolkę (a fragment, który zaczyna się od tego jest z siatki, tak?) W przeciwnym razie po prostu ograniczasz twoja publiczność / baza klientów
JMarsch
4
Może warto zmienić: Func <obiekt, ciąg> na Wyrażenie << Func <obiekt, ciąg >> A jeśli ograniczysz wyrażenie po prawej stronie, aby były tylko stałymi, możesz mieć implementację, która to robi: publiczny statyczny IDictionary <ciąg, ciąg> Hash (parametry Wyrażenie <Func <obiekt, ciąg >> [] skrót) {Słownik <ciąg, ciąg> wartości = nowy Słownik <ciąg, ciąg> (); foreach (var func in hash) {wartości [func.Parameters [0] .Name] = (string) ((ConstantExpression) func.Body) .Value; } zwraca wartości; }
davidfowl
154

Uważam to za dziwne nie tyle z powodu imienia , ale dlatego, że lambda jest niepotrzebna ; mógłby użyć typu anonimowego i być bardziej elastyczny:

.Attributes(new { style = "width:100%", @class="foo", blip=123 });

Jest to wzorzec używany w większości ASP.NET MVC (na przykład) i ma inne zastosowania ( zastrzeżenie , zwróć uwagę również na myśli Ayende, jeśli nazwa jest wartością magiczną, a nie specyficzną dla dzwoniącego)

Marc Gravell
źródło
4
Ma to również problemy z interopem; nie wszystkie języki obsługują tworzenie takiego anonimowego typu w locie.
Brian
5
Uwielbiam to, że nikt tak naprawdę nie odpowiedział na pytanie, zamiast tego ludzie podają argument „to jest lepsze”. : p Czy to nadużycie, czy nie?
Sam Saffron,
7
Byłbym zainteresowany reakcją Erica Lipperta na to. Ponieważ jest w kodzie FRAMEWORK . I to jest tak samo okropne.
Matt Hinze,
26
Nie sądzę, że problemem jest czytelność po napisaniu kodu. Myślę, że prawdziwym problemem jest nauka kodu. Co pomyślisz, kiedy Twoja inteligencja mówi. Atrybuty (obiekt obj) ? Musisz przeczytać dokumentację (której nikt nie chce robić), ponieważ nie będziesz wiedział, co przekazać metodzie. Nie sądzę, żeby było to naprawdę lepsze niż przykład w pytaniu.
NotDan,
2
@Arnis - dlaczego bardziej elastyczny: nie opiera się na domyślnej nazwie parametru, co może (nie cytuj mnie) powodować problemy z niektórymi implementacjami lambda (innymi językami) - ale możesz także używać zwykłych obiektów o zdefiniowanych właściwościach. Na przykład możesz mieć HtmlAttributesklasę z oczekiwanymi atrybutami (dla inteligencji) i po prostu zignoruj ​​te z nullwartościami ...
Marc Gravell
137

Chciałem tylko wrzucić moją opinię (jestem autorem komponentu siatki MvcContrib).

To zdecydowanie nadużycie językowe - bez wątpienia. Jednak tak naprawdę nie uważałbym tego za sprzeczne z intuicją - kiedy spojrzysz na połączenie zAttributes(style => "width:100%", @class => "foo")
, myślę, że to całkiem oczywiste, co się dzieje (z pewnością nie jest gorsze niż podejście typu anonimowego). Z intelektualnej perspektywy zgadzam się, że jest dość nieprzejrzysty.

Dla zainteresowanych kilka podstawowych informacji o jego użyciu w MvcContrib ...

Dodałem to do siatki jako osobistą preferencję - nie lubię używania anonimowych typów jako słowników (posiadanie parametru, który przyjmuje „obiekt” jest tak samo nieprzezroczysty, jak parametr, który przyjmuje parametry Func []), a inicjatorem kolekcji słownika jest raczej gadatliwy (nie jestem też fanem pełnych płynnych interfejsów, np. konieczności łączenia wielu wywołań do atrybutu („styl”, „display: none”). Atrybut („klasa”, „foo”) itp.)

Gdyby C # miał mniej szczegółową składnię literałów słownikowych, nie zawracałbym sobie głowy włączeniem tej składni do komponentu grid :)

Chcę również zauważyć, że użycie tego w MvcContrib jest całkowicie opcjonalne - są to metody rozszerzenia, które owijają przeciążenia, które zamiast tego pobierają IDictionary. Myślę, że ważne jest, aby zapewnić taką metodę, aby wspierać bardziej „normalne” podejście, np. W przypadku współpracy z innymi językami.

Ponadto ktoś wspomniał o „narzutach refleksyjnych”, a ja chciałem tylko zauważyć, że tak naprawdę nie ma narzutów związanych z takim podejściem - nie jest wymagana refleksja środowiska wykonawczego ani kompilacja wyrażeń (patrz http://blog.bittercoder.com /PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx ).

Jeremy Skinner
źródło
1
Próbowałem też bardziej szczegółowo rozwiązać niektóre z poruszonych tutaj problemów na moim blogu: jeremyskinner.co.uk/2009/12/02/lambda-abuse-the-mvccontrib-hash
Jeremy Skinner,
4
Jest nie mniej nieprzezroczysty w Intellisense niż anonimowe obiekty.
Brad Wilson,
22
+1 za wzmiankę, że interfejs został dodany za pomocą opcjonalnych metod rozszerzenia. Użytkownicy niebędący użytkownikami C # (i wszyscy obrażeni przez nadużycie językowe) mogą po prostu zrezygnować z ich używania.
Jørn Schou-Rode
50

wolałbym

Attributes.Add(string name, string value);

Jest o wiele bardziej wyraźny i standardowy i nic nie zyskuje dzięki zastosowaniu lambd.

NotDan
źródło
20
Jest to chociaż? html.Attributes.Add("style", "width:100%");nie czyta tak ładnie jak style = "width:100%"(rzeczywisty wygenerowany HTML), ale style => "width:100%"jest bardzo podobny do tego, jak to wygląda w wynikowym HTML.
Jamie Penney,
6
Ich składnia dopuszcza sztuczki takie jak .Attributes (id => 'foo', @class => 'bar', style => 'width: 100%'). Podpis funkcji używa składni params dla zmiennej liczby argumentów: Atrybuty (params Func <obiekt, obiekt> [] argumenty). Jest bardzo potężny, ale zrozumienie wtf zajęło mi sporo czasu .
Remus Rusanu
27
@Jamie: Próba nadania kodu C # wyglądu kodu HTML byłaby złym powodem do podjęcia decyzji projektowych. Są to zupełnie różne języki do zupełnie innych celów i nie powinny wyglądać tak samo.
Guffa,
17
Równie dobrze mógłby zostać użyty obiekt anonimowy, bez rezygnacji z „piękna”? .Atrybuty (nowy {id = "foo", @class = "bar", style = "szerokość: 100%"})?
Funka
10
@Guffa Dlaczego miałby to być zły powód do podejmowania decyzji projektowych? Dlaczego nie mają wyglądać tak samo? Zgodnie z tym rozumowaniem powinny one celowo wyglądać inaczej? Nie mówię, że się mylisz, po prostu mówię, że możesz chcieć dokładniej rozwinąć swój punkt widzenia.
Samantha Branham
45

Witamy w Rails Land :)

Naprawdę nie ma w tym nic złego, o ile wiesz, co się dzieje. (To wtedy, gdy tego rodzaju rzeczy nie są dobrze udokumentowane, pojawia się problem).

Całość frameworka Railsów opiera się na idei konwencji nad konfiguracją. Nazewnictwo rzeczy w określony sposób wpisuje Cię w konwencję, z której korzystają, a ty dostajesz mnóstwo funkcji za darmo. Przestrzeganie konwencji nazewnictwa prowadzi cię tam, gdzie idziesz szybciej. Wszystko działa świetnie.

Innym miejscem, w którym widziałem taką sztuczkę, są twierdzenia o wywołaniach metod w Moq. Przechodzisz lambda, ale lambda nigdy nie jest wykonywana. Po prostu używają wyrażenia, aby upewnić się, że wywołanie metody się stało, i jeśli nie, zgłaszają wyjątek.

Jason Punyon
źródło
3
Trochę się wahałem, ale zgadzam się. Oprócz narzutu refleksji nie ma znaczącej różnicy między użyciem łańcucha jak w Add () a użyciem nazwy parametru lambda. Przynajmniej o tym myślę. Możesz go zepsuć i wpisać „sytle”, nie zauważając obu stron.
Samantha Branham
5
Nie mogłem zrozumieć, dlaczego nie było to dla mnie dziwne, a potem przypomniałem sobie o Railsach. : D
Allyn
43

To jest okropne na więcej niż jednym poziomie. I nie, to nie jest jak Ruby. To nadużycie C # i .Net.

Pojawiło się wiele sugestii, jak to zrobić w prostszy sposób: krotki, anonimowe typy, płynny interfejs i tak dalej.

Co sprawia, że ​​jest tak źle, że jest to po prostu sposób, aby upodobać sobie:

  • Co się stanie, gdy będziesz musiał zadzwonić z VB?

    .Attributes(Function(style) "width:100%")

  • Jego całkowicie sprzeczna z intuicją inteligencja nie pomoże w ustaleniu, jak przekazać rzeczy.

  • Jest to niepotrzebnie nieefektywne.

  • Nikt nie będzie miał pojęcia, jak go utrzymać.

  • Jakiego rodzaju argumentem są atrybuty? Func<object,string> ? Jak ujawnia się ta intencja. Jaka będzie twoja dokumentacja intelektualna: „Zignoruj ​​wszystkie wartości obiektu”

Myślę, że jesteś całkowicie usprawiedliwiony tym uczuciem odrazy.

Sam Saffron
źródło
4
Powiedziałbym - jest całkowicie intuicyjny. :)
Arnis Lapsa
4
Mówisz, że to nie jest jak Ruby. Ale jest to okropne jak składnia Ruby do określania kluczy i wartości tablicy mieszającej.
Charlie Flowers
3
Kod, który psuje się podczas konwersji alfa! Tak
Phil
3
@Charlie, pod względem składniowym wygląda podobnie, semantycznie jest zupełnie inny.
Sam Saffron
39

Jestem w obozie „blask składni”, jeśli udokumentują to jasno i wygląda to niesamowicie fajnie, prawie nie ma z tym problemu, imo!

Blindy
źródło
10
Amen, bracie. Amen (drugi amen jest wymagany, aby uzyskać minimalną długość komentarzy :)
Charlie Flowers
Twój komentarz był o wiele bardziej niż potrzebny. Ale potem nie możesz po prostu Amen raz, a potem wpisać swój komentarz: D
Sleeper Smith
37

Oboje Jest to nadużycie wyrażeń lambda ORAZ świetności składni.

Arnis Lapsa
źródło
16
Czy to genialne nadużycie składniowe wyrażeń lambda? Chyba się zgadzam :)
Seth Petry-Johnson
21

Prawie nigdy nie spotkałem się z takim zastosowaniem. Myślę, że to „nieodpowiednie” :)

Nie jest to powszechny sposób użycia, jest niezgodny z ogólnymi konwencjami. Ten rodzaj składni ma oczywiście wady i zalety:

Cons

  • Kod nie jest intuicyjny (zwykłe konwencje są różne)
  • Zwykle jest delikatny (zmiana nazwy parametru spowoduje uszkodzenie funkcjonalności).
  • Jest to nieco trudniejsze do przetestowania (sfałszowanie interfejsu API będzie wymagało użycia refleksji w testach).
  • Jeśli intensywne użycie wyrażenia będzie wolniejsze ze względu na potrzebę analizy parametru, a nie tylko wartości (koszt odbicia)

Plusy

  • Jest bardziej czytelny po dostosowaniu się dewelopera do tej składni.

Podsumowując - w publicznym projekcie API wybrałbym bardziej wyraźny sposób.

Elizeusz
źródło
2
@Elisha - twoje plusy i minusy są odwrócone. Przynajmniej mam nadzieję, że nie mówisz, że Pro ma kod „nie intuicyjny”. ;-)
Metro Smurf
W tym konkretnym przypadku - zarówno nazwa parametru lambda, jak i parametr łańcucha są delikatne. To tak, jakby używać dynamicznego do analizowania XML - jest to właściwe, ponieważ i tak nie możesz być pewien XML.
Arnis Lapsa,
19

Nie, z pewnością nie jest to powszechna praktyka. Jest to sprzeczne z intuicją, nie ma sposobu, aby po prostu spojrzeć na kod, aby dowiedzieć się, co on robi. Musisz wiedzieć, w jaki sposób jest używany, aby zrozumieć, w jaki sposób jest używany.

Zamiast podawać atrybuty za pomocą tablicy delegatów, metody łączenia byłyby jaśniejsze i działały lepiej:

.Attribute("style", "width:100%;").Attribute("class", "test")

Chociaż jest to trochę więcej do pisania, jest jasne i intuicyjne.

Guffa
źródło
6
Naprawdę? Wiedziałem dokładnie, co zamierzał ten fragment kodu, gdy na niego spojrzałem. To nie jest takie tępe, chyba że jesteś bardzo surowy. Można podać ten sam argument o przeciążeniu + dla konkatenacji łańcuchów i że zamiast tego powinniśmy zawsze używać metody Concat ().
Samantha Branham
4
@Stuart: Nie, nie wiedziałeś dokładnie, po prostu zgadywałeś na podstawie użytych wartości. Każdy może zgadnąć, ale zgadywanie nie jest dobrym sposobem na zrozumienie kodu.
Guffa,
11
Zgaduję, że używanie .Attribute("style", "width:100%")daje mi style="width:100%", ale o ile wiem, może mi to dać foooooo. Nie widzę różnicy.
Samantha Branham
5
„Zgadywanie na podstawie użytych wartości” ZAWSZE jest to, co robisz patrząc na kod. Jeśli napotkasz wywołanie stream.close (), zakładasz, że zamyka strumień, ale równie dobrze może zrobić coś zupełnie innego.
Wouter Lievens,
1
@ Wouter: Jeśli ZAWSZE zgadujesz podczas czytania kodu, musisz mieć duże trudności z odczytaniem kodu ... Jeśli zobaczę wywołanie metody o nazwie „zamknij”, mogę stwierdzić, że autor klasy nie wie o konwencjach nazewnictwa , więc byłbym bardzo niezdecydowany, by wziąć wszystko za pewnik, co do metody.
Guffa,
17

Czy mogę użyć tego do wykreślenia frazy?

magic lambda (n): funkcja lambda używana wyłącznie w celu zastąpienia magicznego ciągu.

citizenmatt
źródło
tak ... to zabawne. a może to magiczne, w sensie braku bezpieczeństwa czasu kompilacji, czy istnieje miejsce, w którym takie użycie spowodowałoby czas działania zamiast błędów podczas kompilacji?
Masłów
17

Co jest nie tak z następującymi:

html.Attributes["style"] = "width:100%";
Tommy Carlier
źródło
16

Całe to gadanie o „okropnościach” to grupa od dawna przesadnych facetów c # (jestem długoletnim programistą C # i nadal bardzo wielkim fanem języka). W tej składni nie ma nic strasznego. Jest to jedynie próba nadania składni wyglądu bardziej tego, co próbujesz wyrazić. Im mniej „szumu” występuje w składni, tym łatwiej programista może to zrozumieć. Zmniejszenie szumu w jednym wierszu kodu tylko trochę pomaga, ale niech gromadzi się w coraz większej liczbie kodów i okazuje się, że jest to znacząca korzyść.

Jest to próba autora, aby dążyć do tych samych korzyści, które daje DSL - kiedy kod po prostu „wygląda” na to, co próbujesz powiedzieć, osiągnąłeś magiczne miejsce. Możesz debatować, czy jest to dobre dla interop, czy też wystarcza ładniejsze niż anonimowe metody, aby uzasadnić niektóre koszty „złożoności”. W porządku ... więc w swoim projekcie powinieneś dokonać właściwego wyboru, czy użyć tego rodzaju składni. Ale nadal ... jest to sprytna próba programisty zrobienia tego, co na koniec wszyscy próbujemy zrobić (czy zdajemy sobie z tego sprawę, czy nie). A my wszyscy staramy się to zrobić: „Powiedz komputerowi, co chcemy, aby robił w języku, który jest jak najbardziej zbliżony do tego, jak myślimy o tym, co chcemy zrobić”.

Zbliżanie się do wyrażania instrukcji komputerom w taki sam sposób, który naszym zdaniem jest kluczem do uczynienia oprogramowania łatwiejszym w utrzymaniu i dokładniejszym.

EDYCJA: Powiedziałem „klucz do uczynienia oprogramowania łatwiejszym w utrzymaniu i dokładniejszym”, co jest szalenie naiwnym zawyżonym kawałkiem jednorożca. Zmieniłem go na „klucz”.

Charlie Flowers
źródło
12

Jest to jedna z zalet drzewek wyrażeń - można sprawdzić sam kod w celu uzyskania dodatkowych informacji. W ten sposób .Where(e => e.Name == "Jamie")można przekonwertować na równoważną klauzulę SQL Where. To sprytne użycie drzewek wyrażeń, choć mam nadzieję, że nie posunie się ono dalej. Wszystko, co jest bardziej złożone, może być trudniejsze niż kod, który ma zamiar zastąpić, więc podejrzewam, że będzie się samo ograniczać.

Jamie Penney
źródło
To słuszna kwestia, ale prawda w reklamie: LINQ zawiera cały zestaw atrybutów, takich jak TableAttribute i ColumnAttribute, które sprawiają, że jest to bardziej uzasadniona sprawa. Również mapowanie linq sprawdza nazwy klas i właściwości, które są prawdopodobnie bardziej stabilne niż nazwy parametrów.
Remus Rusanu,
Zgadzam się z tobą. Nieznacznie zmieniłem swoje stanowisko w tej sprawie po przeczytaniu tego, co w tej sprawie mieli do powiedzenia Eric Lippert / Anders Helsberg / etc. Pomyślałem, że zostawię tę odpowiedź, ponieważ nadal jest nieco pomocna. Jeśli chodzi o to, co warto, myślę, że ten styl pracy z HTML jest fajny, ale nie pasuje do języka.
Jamie Penney,
7

To ciekawe podejście. Jeśli prawą stronę wyrażenia ograniczyłeś tylko do stałych, możesz zaimplementować użycie

Expression<Func<object, string>>

Myślę, że to, czego naprawdę chcesz zamiast delegata (używasz lambda, aby uzyskać nazwy obu stron) Zobacz naiwną implementację poniżej:

public static IDictionary<string, string> Hash(params Expression<Func<object, string>>[] hash) {
    Dictionary<string, string> values = new Dictionary<string,string>();
    foreach (var func in hash) {
        values[func.Parameters[0].Name] = ((ConstantExpression)func.Body).Value.ToString();
    }
    return values;
}

Może to nawet rozwiązać problem wzajemnych interakcji między językami, o którym wspomniano wcześniej w wątku.

dfowler
źródło
6

Kod jest bardzo sprytny, ale potencjalnie powoduje więcej problemów, które rozwiązuje.

Jak już zauważyłeś, istnieje teraz niejasna zależność między nazwą parametru (stylem) a atrybutem HTML. Sprawdzanie czasu kompilacji nie jest wykonywane. Jeśli nazwa parametru jest błędnie wpisana, strona prawdopodobnie nie będzie zawierała komunikatu o błędzie środowiska wykonawczego, ale znacznie trudniej będzie znaleźć błąd logiczny (brak błędu, ale nieprawidłowe zachowanie).

Lepszym rozwiązaniem byłoby posiadanie elementu danych, który można sprawdzić w czasie kompilacji. Zamiast tego:

.Attributes(style => "width:100%");

kod z właściwością Style może zostać sprawdzony przez kompilator:

.Attributes.Style = "width:100%";

lub nawet:

.Attributes.Style.Width.Percent = 100;

To więcej pracy dla autorów kodu, ale to podejście wykorzystuje silną funkcję sprawdzania typu C #, która pomaga przede wszystkim zapobiegać dostaniu się błędów do kodu.

Chris R. Timmons
źródło
3
Doceniam sprawdzanie czasu kompilacji, ale myślę, że sprowadza się to do opinii. Może coś w stylu new Attributes () {Style: „width: 100%”} przekonałoby do tego więcej osób, ponieważ jest bardziej zwięzłe. Wdrożenie wszystkiego, na co pozwala HTML, jest ogromnym zadaniem i nie mogę winić kogoś za użycie ciągów / lambdas / anonimowych klas.
Samantha Branham
5

rzeczywiście wygląda na to, że Ruby =), przynajmniej dla mnie użycie statycznego zasobu do późniejszego dynamicznego „wyszukiwania” nie pasuje do rozważań dotyczących interfejsu API, mam nadzieję, że ta sprytna sztuczka jest opcjonalna w tym interfejsie.

Możemy dziedziczyć po IDictionary (lub nie) i zapewnić indeksator, który zachowuje się jak tablica php, gdy nie trzeba dodawać klucza, aby ustawić wartość. Będzie to prawidłowe zastosowanie semantyki .net, nie tylko c #, i nadal będzie wymagała dokumentacji.

mam nadzieję że to pomoże

Horacio N. Hdez.
źródło
4

Moim zdaniem jest to nadużycie lambdas.

Jeśli chodzi o świetność składni, uważam to za style=>"width:100%"mylące. Zwłaszcza z powodu =>zamiast=

mfeingold
źródło
4

IMHO, to świetny sposób na zrobienie tego. Wszyscy lubimy fakt, że nazewnictwo kontrolera klasy sprawi, że będzie to kontroler w MVC, prawda? Są więc przypadki, w których nazewnictwo ma znaczenie.

Również intencja jest tutaj bardzo jasna. Bardzo łatwo jest zrozumieć, że .Attribute( book => "something")to spowoduje book="something"i .Attribute( log => "something")spowodujelog="something"

Myślę, że nie powinno to stanowić problemu, jeśli traktujesz to jak konwencję. Uważam, że cokolwiek sprawia, że ​​piszesz mniej kodu, a intencja jest oczywista, jest dobrą rzeczą.

madaboutcode
źródło
4
Nazywanie klasy Kontroler nie będzie kucał, jeśli nie odziedziczysz też po kontrolerze ...
Jordan Wallwork,
3

Jeśli nazwy metod (func) są dobrze wybrane, jest to świetny sposób na uniknięcie problemów związanych z konserwacją (np. Dodanie nowego func, ale zapomniałem dodać go do listy mapowania parametrów funkcji). Oczywiście musisz to mocno udokumentować i lepiej wygeneruj dokumentację dla parametrów z dokumentacji dla funkcji w tej klasie ...

Craig Trader
źródło
1

Myślę, że nie jest to lepsze niż „magiczne sznurki”. Nie jestem też fanem anonimowych typów. Wymaga lepszego i silnie typowanego podejścia.

JamesK
źródło