Nazwa parametru: nadużywanie wyrażeń C # lambda czy składnia?

Patrzę na komponent MvcContrib Grid i jestem zafascynowany, ale jednocześnie odpychany, sztuczką składniową używaną w składni Grid :

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

Powyższa składnia ustawia atrybut style generowanego HTML na width:100%. Teraz, jeśli zwrócisz uwagę, 'styl' nie jest nigdzie określony. Wynika to z nazwy parametru w wyrażeniu! Musiałem się w to zagłębić i znaleźć miejsce, gdzie dzieje się "magia":

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

Więc rzeczywiście, kod używa formalnej, czas kompilacji, nazwy parametrów do tworzenia słownika par atrybutów nazwa-wartość. Wynikająca z tego konstrukcja składniowa jest rzeczywiście bardzo ekspresyjna, ale jednocześnie bardzo niebezpieczna.

Ogólne użycie wyrażeń lambda pozwala na zastąpienie nazw używanych bez efektu ubocznego. Widzę przykład w książce, która mówi collection.ForEach(book => Fire.Burn(book)) wiem, że mogę napisać w moim kodzie collection.ForEach(log => Fire.Burn(log)) i oznacza to samo. Ale z siatką MvcContrib składnia tutaj nagle znajduję kod, który aktywnie wygląda i podejmuje decyzje w oparciu o nazwy, które wybieram dla moich zmiennych!

Więc jest to powszechna praktyka ze społecznością C# 3.5/4.0 i miłośnikami wyrażeń lambda? A może nie powinienem się martwić o jednego łotra, Mavericka?

Author: Remus Rusanu, 2009-11-11

21 answers

To ma słabe interop. Na przykład, rozważmy ten przykład C # - F #

C#:

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

F#:

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

Wynik:

Drukowane jest"arg" (nie "yadda").

W rezultacie projektanci bibliotek powinni albo unikać tego rodzaju 'nadużyć', albo przynajmniej zapewnić' standardowe ' przeciążenie (np., że bierze nazwę Łańcucha jako dodatkowy parametr), jeśli chcą mieć dobre interopy między językami.Net.

 148
Author: Brian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 21:17:48

Uważam, że to dziwne nie tyle ze względu na nazwę , ale dlatego, że lambda jest niepotrzebna ; przydałby się anonimowy typ i byłby bardziej elastyczny:

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

Jest to wzór używany w wielu ASP.NET na przykład, i ma inne zastosowania (a zastrzeżenie , zauważ również myśli Ayende Jeśli nazwa jest magiczną wartością, a nie specyficzną dla rozmówcy)

 154
Author: Marc Gravell,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 21:35:26

Chciałem tylko dorzucić swoją opinię (jestem autorem komponentu MvcContrib grid).

To zdecydowanie nadużywanie języka-bez wątpienia. Jednak nie uważałbym tego za intuicyjne - patrząc na wywołanie do Attributes(style => "width:100%", @class => "foo")
Myślę, że to dość oczywiste, co się dzieje (na pewno nie jest gorsze niż anonimowe podejście typu). Z perspektywy intellisense, zgadzam się, że jest dość nieprzejrzysty.

Dla zainteresowanych kilka podstawowych informacji na temat jego wykorzystania w MvcContrib...

Dodałem to do siatki jako osobiste preferencje - nie podoba mi się używanie anonimowych typów jako słowników (posiadanie parametru, który przyjmuje "object" jest tak samo nieprzezroczyste, jak ten, który przyjmuje params Func []), a Dictionary collection initializer jest raczej gadatliwy (nie jestem również fanem gadatliwych interfejsów, np. konieczności łączenia wielu wywołań do atrybutu ("style", "display:none").Atrybut ("class", "foo") etc)

If C# has a less verbose syntax for literały słownikowe, wtedy nie zawracałbym sobie głowy włączaniem tej składni do komponentu grid:)

Chcę również podkreślić, że użycie tego w MvcContrib jest całkowicie opcjonalne - są to metody rozszerzenia, które owijają przeciążenia, które zamiast tego przyjmują IDictionary. Myślę, że to ważne, że jeśli podasz metodę taką jak ta, powinieneś również wspierać bardziej "normalne" podejście, np. dla interop z innymi językami.

Też ktoś wspomniał o "odbiciu nad głową" I po prostu chciałem zauważyć, że tak naprawdę nie ma zbyt wiele narzutu z tym podejściem - nie ma żadnego odzwierciedlenia lub kompilacji wyrażeń (patrz http://blog.bittercoder.com/PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx).
 138
Author: Jeremy Skinner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-01 04:31:48

Wolałbym

Attributes.Add(string name, string value);

Jest znacznie bardziej wyrazisty i standardowy i nic nie zyskuje się używając lambda.

 50
Author: NotDan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-08 10:42:53

Witaj W Rails Land :)

Nie ma w tym nic złego, dopóki wiesz, o co chodzi. (To wtedy, gdy tego rodzaju rzeczy nie są dobrze udokumentowane, że jest problem).

Cały framework Rails jest zbudowany na idei konwencji nad konfiguracją. Nazywanie rzeczy w określony sposób wprowadza cię w konwencję, której używają i dostajesz za darmo mnóstwo funkcjonalności. Postępując zgodnie z konwencją nazewnictwa, dojdziesz tam, gdzie jedziesz szybciej. Na wszystko działa genialnie.

Innym miejscem, gdzie widziałem taki trik jest w method call assertions w Moq. Mijasz lambdę, ale lambda nigdy nie jest stracona. Po prostu używają wyrażenia, aby upewnić się, że wywołanie metody się stało i rzucają wyjątek, jeśli nie.

 45
Author: Jason Punyon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-03 18:02:32

To jest straszne na więcej niż jednym poziomie. I nie, to w niczym nie przypomina Ruby. Jest 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.

To, co sprawia, że jest tak źle, to to, że jest po prostu sposób na fantazję dla własnego dobra:

  • Co się dzieje, gdy trzeba to wywołać z Visual Basic?

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

  • Jest całkowicie przeciwny intuicyjny i intellisense niewiele pomoże dowiedzieć się, jak przekazać rzeczy.

  • To niepotrzebnie nieefektywne.

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

  • Jaki jest typ argumentu wchodzącego do atrybutów? czy to Func<object,string>? W jaki sposób ta intencja jest ujawniająca? Jaka jest Twoja dokumentacja intellisense, która powie :" proszę lekceważyć wszystkie wartości obiektu"?

Myślę, że jesteś całkowicie usprawiedliwiony mając to uczucie odrazy.
 43
Author: Sam Saffron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-15 22:36:46

Jestem w obozie "błyskotliwość składni", jeśli dokumentują to wyraźnie, i wygląda to tak cholernie fajnie, prawie nie ma z tym problemu imo!

 40
Author: Blindy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 21:07:23

Obie. Jest to abusage wyrażeń lambda i składni .

 37
Author: Arnis Lapsa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-02 08:21:19

Prawie nigdy nie natknąłem się na tego typu użycie. Myślę, że to "niestosowne":)

Nie jest to powszechny sposób użycia, jest niezgodny z ogólnymi konwencjami. Taka składnia ma oczywiście plusy i minusy:

Cons

  • kod nie jest intuicyjny (zwykłe konwencje są różne)
  • wydaje się być kruchy(zmiana nazwy parametru spowoduje złamanie funkcjonalności).
  • jest nieco trudniejszy do przetestowania (udawanie API będzie wymagało użycia refleksji w testach).
  • jeśli wyrażenie jest używane intensywnie, będzie wolniejsze ze względu na konieczność analizy parametru, a nie tylko wartości (koszt odbicia)

Plusy

  • jest bardziej czytelny po dostosowaniu programisty do tej składni.

Bottom line - W public API design wybrałbym bardziej wyraźny sposób.

 21
Author: Elisha,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-12 04:16:16

Nie, to z pewnością nie jest powszechna praktyka. Jest to intuicyjne, nie ma sposobu, aby po prostu spojrzeć na kod, aby dowiedzieć się, co robi. Musisz wiedzieć, jak to jest używane, aby zrozumieć, jak to jest używane.

Zamiast dostarczania atrybutów za pomocą tablicy delegatów, metody łańcuchowania byłyby jaśniejsze i działały lepiej:

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

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

 19
Author: Guffa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 21:13:12

Czy mogę użyć tego do napisania frazy?

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

 18
Author: citizenmatt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-03 16:01:35

Co jest nie tak z:

html.Attributes["style"] = "width:100%";
 17
Author: Tommy Carlier,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-07 12:00:52

Całe to gadanie o "okropności" to banda długoletnich c# facetów przesadzających (a ja jestem długoletnim programistą C# i nadal jestem wielkim fanem języka). Nie ma nic strasznego w tej składni. Jest to tylko próba, aby składnia wyglądała bardziej jak to, co próbujesz wyrazić. Im mniej "szumu" jest w składni czegoś, tym łatwiej programista może to zrozumieć. Zmniejszenie szumu w jednej linijce kodu tylko trochę pomaga, ale niech to będzie więcej i więcej kodu, a okazuje się to znaczną korzyścią.

Jest to próba autora, aby dążyć do tych samych korzyści, które dają Ci DSL - gdy kod po prostu "wygląda" to, co próbujesz powiedzieć, dotarłeś do magicznego miejsca. Można dyskutować, czy jest to dobre dla interop, czy też jest wystarczająco ładniejsze niż metody anonimowe, aby uzasadnić niektóre koszty "złożoności". W porządku ... tak więc w Twoim projekcie powinieneś dokonać właściwego wyboru, czy używać tego rodzaju składni. Ale jednak ... jest to sprytna próba programisty, aby zrobić to, co w końcu wszyscy próbujemy zrobić (niezależnie od tego, czy zdajemy sobie z tego sprawę, czy nie). A to, co wszyscy staramy się zrobić, to: "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, aby robił."

Zbliżanie się do wyrażania naszych instrukcji dla komputerów w taki sam sposób, jak uważamy, że wewnętrznie jest kluczem do uczynienia oprogramowania bardziej łatwym do utrzymania i bardziej dokładnie.

EDIT: powiedziałem "klucz do uczynienia oprogramowania bardziej łatwym do utrzymania i dokładniejszym", co jest szalenie naiwną, zawyżoną odrobiną jednoroczności. Zmieniłem na " klucz."

 16
Author: Charlie Flowers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-08-17 06:41:24

Jest to jedna z zalet drzewa wyrażeń - można zbadać sam kod w celu uzyskania dodatkowych informacji. W ten sposób .Where(e => e.Name == "Jamie") można przekształcić do równoważnej klauzuli SQL Where. Jest to sprytne użycie drzew ekspresji, choć mam nadzieję, że nie pójdzie dalej niż to. Wszystko bardziej złożone może być trudniejsze niż kod, który ma zastąpić, więc podejrzewam, że będzie samoograniczające.

 12
Author: Jamie Penney,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 21:06:30

To ciekawe podejście. Jeśli prawa strona wyrażenia jest tylko stała, to można zaimplementować za pomocą

Expression<Func<object, string>>

Co myślę, że jest 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;
}

To może nawet rozwiązać problem cross language interop, który został wspomniany wcześniej w wątku.

 7
Author: dfowler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-02-22 09:42:04

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 (styl) a atrybutem HTML. Nie jest wykonywane sprawdzanie czasu kompilacji. Jeśli nazwa parametru jest błędnie wpisana, prawdopodobnie strona nie będzie miała Komunikatu o błędzie runtime, ale o wiele trudniejszy do znalezienia błąd logiczny(brak błędu, ale nieprawidłowe zachowanie).

Lepszym rozwiązaniem byłoby posiadanie elementu danych, który można sprawdzić w czas kompilacji. Więc zamiast tego:

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

Kod z właściwością Style może być sprawdzany 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ą zdolność sprawdzania typów w C#, która pomaga zapobiegać przedostawaniu się błędów do kodu.

 6
Author: Chris R. Timmons,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 21:23:00

Rzeczywiście wygląda jak Ruby=), przynajmniej dla mnie użycie statycznego zasobu do późniejszego dynamicznego "wyszukiwania" nie pasuje do kwestii projektowania api, mam nadzieję, że ta sprytna sztuczka jest opcjonalna w tym api.

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

Hope this helps

 5
Author: Horacio N. Hdez.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 22:30:34

Moim zdaniem jest to nadużycie lambda.

Co do blasku składni uważam style=>"width:100%" za zwyczajnie mylące. Szczególnie ze względu na => zamiast =

 4
Author: mfeingold,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 21:20:01

IMHO, to fajny sposób na to. Wszyscy uwielbiamy fakt, że nazwanie kontrolera klasy uczyni go kontrolerem w MVC, prawda? Są więc przypadki, w których nazewnictwo ma znaczenie.

Intencja jest tutaj bardzo jasna. Bardzo łatwo zrozumieć, że .Attribute( book => "something") spowoduje book="something" i .Attribute( log => "something") spowoduje log="something" Myślę, że nie powinno być problemu, jeśli traktujesz to jak konwencję. Jestem zdania, że cokolwiek sprawia, że piszesz mniej kodu i sprawia, że intencja oczywiste to dobra rzecz.
 4
Author: madaboutcode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-02 16:43:43

Jeśli nazwy metody (func) są dobrze dobrane, to jest to świetny sposób na uniknięcie bólów głowy związanych z konserwacją (np.: Dodaj nowy func, ale zapomniałeś dodać go do listy mapowania funkcji-parametrów). Oczywiście, trzeba to mocno udokumentować i lepiej automatycznie generować dokumentację dla parametrów z dokumentacji dla funkcji w tej klasie...

 3
Author: Craig Trader,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-11 21:18:30

Myślę, że to nie jest lepsze niż "magiczne struny". Nie przepadam też za anonimowymi typami. Wymaga lepszego i mocno wpisanego podejścia.

 1
Author: JamesK,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-07 11:43:45