Reflektierender Parametername: Missbrauch von C # -Lambda-Ausdrücken oder Syntaxbrillanz?

428

Ich betrachte die MvcContrib Grid-Komponente und bin fasziniert und gleichzeitig abgestoßen von einem syntaktischen Trick, der in der Grid-Syntax verwendet wird :

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

Die obige Syntax setzt das Stilattribut des generierten HTML auf width:100%. Wenn Sie nun darauf achten, wird 'Stil' nirgends angegeben und aus dem Namen des Parameters im Ausdruck abgeleitet! Ich musste mich damit befassen und herausfinden, wo die 'Magie' passiert:

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

In der Tat verwendet der Code den formalen Namen der Parameter zur Kompilierungszeit, um das Wörterbuch der Attribut-Name-Wert-Paare zu erstellen. Das resultierende Syntaxkonstrukt ist zwar sehr ausdrucksstark, aber gleichzeitig sehr gefährlich. Die allgemeine Verwendung von Lambda-Ausdrücken ermöglicht das Ersetzen der verwendeten Namen ohne Nebenwirkungen. Ich sehe ein Beispiel in einem Buch, das besagt, dass collection.ForEach(book => Fire.Burn(book))ich weiß , dass ich in meinen Code schreiben kann, collection.ForEach(log => Fire.Burn(log))und es bedeutet dasselbe . Aber mit der MvcContrib Grid-Syntax hier finde ich plötzlich Code, der aktiv nach den Namen sucht und Entscheidungen trifft, die ich für meine Variablen wähle!

Ist dies also eine gängige Praxis bei der C # 3.5 / 4.0-Community und den Liebhabern von Lambda-Ausdrücken? Oder ist ein Schurke ein Trick-Außenseiter, über den ich mir keine Sorgen machen sollte?

Remus Rusanu
quelle
34
Ich würde argumentieren, dass dies offensichtlich erscheint, solange Sie bereit sind, die Absicht des Codes zu betrachten, anstatt nur die Syntax zu analysieren. Was Sie sowieso tun sollten, wenn Sie guten Code lesen. Syntax ist nur ein Mittel zur Absicht, und ich würde argumentieren, dass dies Absicht ist, Code zu enthüllen.
Jamie Penney
190
Ich habe gerade Anders (und den Rest des Designteams) gefragt, was sie denken. Sagen wir einfach, die Ergebnisse könnten nicht in einer familienfreundlichen Zeitung gedruckt werden.
Eric Lippert
22
In C # fehlt derzeit eine saubere, leichte Syntax für Karten, insbesondere für Karten, die an Funktionen übergeben werden. Verschiedene dynamische Sprachen (Ruby, Python, JavaScript, ColdFusion 9) haben dafür eine saubere, leichte Syntax.
Yfeldblum
28
Oh, ich finde es sieht herrlich aus. Was eine Syntax für Maps angeht, wäre es großartig, wenn wir neue {{"Do", "A Deer"}, {"Re", "Golden Sun"} ...} machen und den Compiler die ableiten lassen könnten Konstruktion einer Karte, genau wie neu [] {1, 2,3} die Konstruktion eines int-Arrays leitet. Wir denken über solche Dinge nach.
Eric Lippert
17
Eric Lippert, ich respektiere Sie sehr, aber ich denke, Sie und die Gruppe (einschließlich Anders, den ich FREAKIN 'TREMENDOUSLY respektiere) schlagen dies zu hart zu. Wie Sie zugeben, fehlt C # eine enge Syntax für Maps, und einige andere langs (wie Ruby) haben großartige. Dieser Typ hat einen Weg gefunden, die gewünschte Syntax zu erhalten. Ich gebe Ihnen zu, dass es ähnliche Ausdrucksmöglichkeiten gibt, die fast so ausdrucksstark sind wie seine Syntax mit weniger Nachteilen. Aber seine Syntax und die Tatsache, dass er so hart gearbeitet hat, um es zu bekommen, zeigt deutlich, dass eine Sprachverbesserung erforderlich ist. Die Leistung in der Vergangenheit zeigt, dass ihr etwas Großartiges dafür schaffen werdet.
Charlie Flowers

Antworten:

147

Dies hat eine schlechte Interop. Betrachten Sie beispielsweise dieses C # - F # -Beispiel

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")

Ergebnis:

"arg" wird gedruckt (nicht "yadda").

Aus diesem Grund sollten Bibliotheksdesigner diese Art von "Missbrauch" entweder vermeiden oder zumindest eine "Standard" -Überladung bereitstellen (z. B. wenn der Zeichenfolgenname als zusätzlicher Parameter verwendet wird), wenn sie eine gute Interop zwischen .NET-Sprachen wünschen.

Brian
quelle
11
Das tust du nicht. Diese Strategie ist einfach nicht portierbar. (Falls dies beispielsweise hilfreich ist, kann F # Methoden überladen, die sich nur im Rückgabetyp unterscheiden (Typinferenz kann dies tun). Dies kann in der CLR ausgedrückt werden. F # lässt dies jedoch nicht zu, hauptsächlich weil if Wenn Sie dies getan haben, können diese APIs nicht über C # aufgerufen werden.) Wenn es um Interop geht, gibt es immer Kompromisse bei den Randfunktionen hinsichtlich der Vorteile, die Sie gegenüber dem Interop erhalten, mit dem Sie handeln.
Brian
32
Ich mag Nicht-Interoperabilität nicht als Grund, etwas nicht zu tun. Wenn Interoperabilität erforderlich ist, tun Sie dies, wenn nicht, warum sollten Sie sich dann darum kümmern? Das ist YAGNI IMHO.
John Farrell
15
@jfar: In .NET CLR hat die Landinteroperabilität eine völlig neue Dimension, da in einem Compiler generierte Assemblys aus einer anderen Sprache verwendet werden sollen.
Remus Rusanu
25
Ich bin damit einverstanden, dass Sie nicht CLS-konform sein müssen, aber es scheint eine gute Idee zu sein, wenn Sie eine Bibliothek oder ein Steuerelement schreiben (und das Snippet, mit dem dies beginnt, stammt aus einem Raster, ja?). Andernfalls beschränken Sie sich nur Ihr Publikum / Kundenstamm
JMarsch
4
Vielleicht lohnt es sich, Folgendes zu ändern: Func <Objekt, Zeichenfolge> in Ausdruck << Func <Objekt, Zeichenfolge >> Und wenn Sie die rechte Seite des Ausdrucks auf Konstanten beschränken, können Sie eine Implementierung haben, die dies tut: public static IDictionary <string, string> Hash (params Ausdruck <Func <Objekt, string >> [] Hash) {Dictionary <string, string> values ​​= new Dictionary <string, string> (); foreach (var func in hash) {values ​​[func.Parameters [0] .Name] = (string) ((ConstantExpression) func.Body) .Value; } Rückgabewerte; }
Davidfowl
154

Ich finde das seltsam nicht so sehr wegen des Namens , sondern weil das Lambda unnötig ist ; Es könnte einen anonymen Typ verwenden und flexibler sein:

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

Dies ist ein Muster, das in einem Großteil von ASP.NET MVC (zum Beispiel) verwendet wird und andere Verwendungszwecke hat (eine Einschränkung , beachten Sie auch Ayendes Gedanken, wenn der Name eher ein magischer als ein anruferspezifischer Wert ist).

Marc Gravell
quelle
4
Dies hat auch Interop-Probleme; Nicht alle Sprachen unterstützen die Erstellung eines solchen anonymen Typs im laufenden Betrieb.
Brian
5
Ich liebe es, wie niemand die Frage wirklich beantwortet hat, stattdessen liefern die Leute ein "das ist besser" Argument. : p Ist es ein Missbrauch oder nicht?
Sam Saffron
7
Es würde mich interessieren, wie Eric Lippert darauf reagiert. Weil es im FRAMEWORK-Code ist . Und es ist genauso schrecklich.
Matt Hinze
26
Ich glaube nicht, dass das Problem die Lesbarkeit ist, nachdem der Code geschrieben wurde. Ich denke, das eigentliche Problem ist die Lernfähigkeit des Codes. Was wirst du denken, wenn dein Intellisense .Attributes (object obj) sagt ? Sie müssen die Dokumentation lesen (was niemand tun möchte), da Sie nicht wissen, was Sie an die Methode übergeben sollen. Ich denke nicht, dass dies wirklich besser ist als das Beispiel in der Frage.
NotDan
2
@Arnis - warum flexibler: es ist nicht auf dem impliziten Parameternamen setzen, die Macht (zitieren Sie mich nicht) Ursache Probleme mit einigen Lambda - Implementierungen (andere Sprachen) - Sie können aber auch normale Objekte mit definierten Eigenschaften verwenden. Zum Beispiel könnten Sie eine HtmlAttributesKlasse mit den erwarteten Attributen (für Intellisense) haben und diese mit nullWerten einfach ignorieren ...
Marc Gravell
137

Ich wollte nur meine Meinung einbringen (ich bin der Autor der MvcContrib-Grid-Komponente).

Dies ist definitiv Sprachmissbrauch - kein Zweifel. Ich würde es jedoch nicht wirklich als kontraintuitiv betrachten - wenn Sie sich einen Anruf bei ansehen, Attributes(style => "width:100%", @class => "foo")
denke ich, dass es ziemlich offensichtlich ist, was los ist (es ist sicherlich nicht schlimmer als der anonyme Ansatz). Aus einer intelligisense Perspektive stimme ich zu, dass es ziemlich undurchsichtig ist.

Für Interessierte einige Hintergrundinformationen zur Verwendung in MvcContrib ...

Ich habe dies aus persönlichen Gründen zum Raster hinzugefügt. Ich mag die Verwendung anonymer Typen als Wörterbücher nicht (ein Parameter, der "Objekt" annimmt, ist genauso undurchsichtig wie einer, der Parameter Func [] verwendet) und der Initialisierer der Wörterbuchsammlung ziemlich ausführlich (ich bin auch kein Fan von ausführlichen, fließenden Schnittstellen, z. B. wenn mehrere Aufrufe an ein Attribut ("Stil", "Anzeige: keine") verkettet werden müssen. Attribut ("Klasse", "foo") usw.)

Wenn C # eine weniger ausführliche Syntax für Wörterbuchliterale hätte, hätte ich mir nicht die Mühe gemacht, diese Syntax in die Rasterkomponente aufzunehmen :)

Ich möchte auch darauf hinweisen, dass die Verwendung in MvcContrib völlig optional ist - dies sind Erweiterungsmethoden, die Überladungen umschließen, die stattdessen ein IDictionary verwenden. Ich denke, es ist wichtig, dass Sie, wenn Sie eine Methode wie diese bereitstellen, auch einen „normaleren“ Ansatz unterstützen, z. B. für die Interaktion mit anderen Sprachen.

Außerdem erwähnte jemand den "Reflection Overhead" und ich wollte nur darauf hinweisen, dass dieser Ansatz wirklich keinen großen Overhead verursacht - es ist keine Laufzeitreflexion oder Ausdruckskompilierung erforderlich (siehe http://blog.bittercoder.com) /PermaLink,guid,206e64d1-29ae-4362-874b-83f5b103727f.aspx ).

Jeremy Skinner
quelle
1
Ich habe auch versucht, einige der hier angesprochenen Probleme in meinem Blog ausführlicher zu behandeln
Jeremy Skinner
4
In Intellisense ist es nicht weniger undurchsichtig als anonyme Objekte.
Brad Wilson
22
+1 für die Erwähnung, dass die Schnittstelle über optionale Erweiterungsmethoden hinzugefügt wird . Nicht-C # -Benutzer (und alle, die durch den Sprachmissbrauch beleidigt sind) können einfach darauf verzichten.
Jørn Schou-Rode
50

ich würde bevorzugen

Attributes.Add(string name, string value);

Es ist viel expliziter und standardisierter und durch die Verwendung von Lambdas wird nichts erreicht.

NotDan
quelle
20
Ist es trotzdem? html.Attributes.Add("style", "width:100%");liest sich nicht so gut wie style = "width:100%"(das tatsächlich erzeugte HTML), wohingegen style => "width:100%"sehr nahe daran ist, wie es im resultierenden HTML aussieht.
Jamie Penney
6
Ihre Syntax erlaubt Tricks wie .Attributes (id => 'foo', @class => 'bar', style => 'width: 100%'). Die Funktionssignatur verwendet die Parameter-Syntax für die variable Anzahl von Argumenten: Attribute (Parameter Func <Objekt, Objekt> [] Argumente). Es ist sehr mächtig, aber ich habe eine ganze Weile gebraucht, um zu verstehen, was es tut.
Remus Rusanu
27
@Jamie: Der Versuch, den C # -Code wie den HTML-Code aussehen zu lassen, wäre ein schlechter Grund für Entwurfsentscheidungen. Sie sind völlig unterschiedliche Sprachen für völlig unterschiedliche Zwecke und sollten nicht gleich aussehen.
Guffa
17
Ein anonymes Objekt hätte genauso gut verwendet werden können, ohne die "Schönheit" zu opfern? .Attributes (new {id = "foo", @class = "bar", style = "width: 100%"}) ??
Funka
10
@Guffa Warum sollte es ein schlechter Grund für Designentscheidungen sein? Warum sollten sie nicht gleich aussehen? Sollten sie nach dieser Überlegung absichtlich anders aussehen? Ich sage nicht, dass Sie falsch liegen, ich sage nur, dass Sie Ihren Standpunkt vielleicht genauer erläutern möchten.
Samantha Branham
45

Willkommen im Rails Land :)

Es ist wirklich nichts falsch daran, solange Sie wissen, was los ist. (Wenn so etwas nicht gut dokumentiert ist, liegt ein Problem vor).

Das gesamte Rails-Framework basiert auf der Idee der Konvention über die Konfiguration. Wenn Sie Dinge auf eine bestimmte Weise benennen, gelangen Sie zu einer Konvention, die sie verwenden, und Sie erhalten eine ganze Reihe von Funktionen kostenlos. Wenn Sie die Namenskonvention befolgen, gelangen Sie schneller dahin, wo Sie hin wollen. Das Ganze funktioniert hervorragend.

Ein anderer Ort, an dem ich einen solchen Trick gesehen habe, sind Methodenaufruf-Assertions in Moq. Sie geben ein Lambda ab, aber das Lambda wird nie ausgeführt. Sie verwenden nur den Ausdruck, um sicherzustellen, dass der Methodenaufruf stattgefunden hat, und lösen eine Ausnahme aus, wenn nicht.

Jason Punyon
quelle
3
Ich war etwas zögerlich, aber ich stimme zu. Abgesehen vom Reflexionsaufwand gibt es keinen signifikanten Unterschied zwischen der Verwendung einer Zeichenfolge wie in Add () und der Verwendung eines Lambda-Parameternamens. Zumindest kann ich mir das vorstellen. Sie können es vermasseln und "sytle" eingeben, ohne es in beide Richtungen zu bemerken.
Samantha Branham
5
Ich konnte nicht herausfinden, warum mir das nicht komisch war, und dann erinnerte ich mich an Rails. : D
Allyn
43

Das ist auf mehr als einer Ebene schrecklich . Und nein, das ist nichts wie Ruby. Es ist ein Missbrauch von C # und .Net.

Es gab viele Vorschläge, wie dies einfacher zu bewerkstelligen ist: Tupel, anonyme Typen, eine fließende Benutzeroberfläche und so weiter.

Was es so schlimm macht, ist, dass es nur ein Weg ist, sich etwas Gutes zu tun:

  • Was passiert, wenn Sie dies von VB aus aufrufen müssen?

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

  • Die völlig kontraintuitive, intelligente Intelligenz wird wenig dazu beitragen, herauszufinden, wie man Dinge weitergibt.

  • Es ist unnötig ineffizient.

  • Niemand wird eine Ahnung haben, wie man es aufrechterhält.

  • Was ist die Art des Arguments, das in Attribute eingeht, oder Func<object,string>? Wie offenbart sich diese Absicht? In Ihrer Intellisense-Dokumentation heißt es: "Bitte ignorieren Sie alle Werte des Objekts."

Ich denke, Sie sind völlig berechtigt, diese Gefühle der Abneigung zu haben.

Sam Safran
quelle
4
Ich würde sagen - es ist völlig intuitiv. :)
Arnis Lapsa
4
Sie sagen, es ist nichts wie Ruby. Aber es ist sehr ähnlich wie Rubys Syntax, die Schlüssel und Werte einer Hashtabelle anzugeben.
Charlie Flowers
3
Code, der bei der Alpha-Konvertierung kaputt geht! Ja!
Phil
3
@Charlie, syntaktisch sieht es ähnlich aus, semantisch ist es ganz anders.
Sam Saffron
39

Ich bin im Lager "Syntax Brilliance", wenn sie es klar dokumentieren und es so verdammt cool aussieht, gibt es imo fast kein Problem damit!

Blind
quelle
10
Amen, Bruder. Amen (2. Amen erforderlich, um die Mindestlänge für Kommentare zu erreichen :)
Charlie Flowers
Ihr Kommentar allein war weit mehr als nötig. Aber dann können Sie nicht nur Amen einmal und dann Ihren Kommentar eingeben: D
Sleeper Smith
37

Beide. Es ist der Missbrauch von Lambda-Ausdrücken UND Syntax-Brillanz.

Arnis Lapsa
quelle
16
Es ist also ein brillanter syntaktischer Missbrauch von Lambda-Ausdrücken? Ich denke, ich stimme zu :)
Seth Petry-Johnson
21

Ich bin kaum auf diese Art der Verwendung gestoßen. Ich finde es "unangemessen" :)

Dies ist keine übliche Verwendung, sie widerspricht den allgemeinen Konventionen. Diese Art der Syntax hat natürlich Vor- und Nachteile:

Nachteile

  • Der Code ist nicht intuitiv (übliche Konventionen sind unterschiedlich)
  • Es neigt dazu, zerbrechlich zu sein (das Umbenennen des Parameters beeinträchtigt die Funktionalität).
  • Das Testen ist etwas schwieriger (das Fälschen der API erfordert die Verwendung von Reflexion in Tests).
  • Wenn der Ausdruck intensiv verwendet wird, ist er langsamer, da der Parameter und nicht nur der Wert (Reflexionskosten) analysiert werden müssen.

Vorteile

  • Es ist besser lesbar, nachdem der Entwickler diese Syntax angepasst hat.

Fazit : Im öffentlichen API-Design hätte ich einen expliziteren Weg gewählt.

Elisha
quelle
2
@ Elisha - Ihre Vor- und Nachteile sind umgekehrt. Zumindest hoffe ich, dass Sie nicht sagen, dass ein Pro Code "nicht intuitiv" hat. ;-)
Metro Smurf
In diesem speziellen Fall sind sowohl der Lambda-Parametername als auch der Zeichenfolgenparameter fragil. Es ist wie die Verwendung von Dynamic für das XML-Parsing - es ist angemessen, da Sie sich über XML sowieso nicht sicher sein können.
Arnis Lapsa
19

Nein, das ist sicherlich keine gängige Praxis. Es ist nicht intuitiv, es gibt keine Möglichkeit, nur den Code zu betrachten, um herauszufinden, was er tut. Sie müssen wissen, wie es verwendet wird, um zu verstehen, wie es verwendet wird.

Anstatt Attribute mithilfe eines Arrays von Delegaten bereitzustellen, wären Verkettungsmethoden klarer und leistungsfähiger:

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

Obwohl dies etwas mehr zu tippen ist, ist es klar und intuitiv.

Guffa
quelle
6
"Ja wirklich?" Ich wusste genau, was dieser Codeausschnitt bedeutete, als ich ihn mir ansah. Es ist nicht so stumpf, wenn Sie nicht sehr streng sind. Man könnte das gleiche Argument über das Überladen von + für die Verkettung von Zeichenfolgen geben und dass wir stattdessen immer eine Concat () -Methode verwenden sollten.
Samantha Branham
4
@Stuart: Nein, du wusstest nicht genau, du hast nur anhand der verwendeten Werte geraten. Jeder kann diese Vermutung anstellen, aber das Erraten ist kein guter Weg, um Code zu verstehen.
Guffa
11
Ich vermute, mit .Attribute("style", "width:100%")gibt gibt mir style="width:100%", aber soweit ich weiß, könnte es mir geben foooooo. Ich sehe den Unterschied nicht.
Samantha Branham
5
"Erraten basierend auf den verwendeten Werten" ist IMMER das, was Sie tun, wenn Sie sich Code ansehen. Wenn Sie auf einen Aufruf von stream.close () stoßen, gehen Sie davon aus, dass er einen Stream schließt, aber genauso gut etwas völlig anderes tun kann.
Wouter Lievens
1
@Wouter: Wenn Sie beim Lesen von Code IMMER raten, müssen Sie große Schwierigkeiten haben, Code zu lesen ... Wenn ich einen Aufruf einer Methode mit dem Namen "close" sehe, kann ich feststellen, dass der Autor der Klasse nichts über Namenskonventionen weiß Daher würde ich sehr zögern, irgendetwas als selbstverständlich zu betrachten, was die Methode bewirkt.
Guffa
17

Kann ich damit eine Phrase prägen?

magisches Lambda (n): Eine Lambda-Funktion, die ausschließlich zum Ersetzen einer magischen Zeichenfolge verwendet wird.

bürgermatt
quelle
Ja ... das ist lustig. und vielleicht ist es magisch, im Sinne einer fehlenden Sicherheit bei der Kompilierung, gibt es einen Ort, an dem diese Verwendung Laufzeitfehler anstelle von Fehlern bei der Kompilierung verursachen würde?
Maslow
17

Was ist los mit folgendem:

html.Attributes["style"] = "width:100%";
Tommy Carlier
quelle
16

All diese Scherze über "Horror" sind ein Haufen langjähriger C # -Typen, die überreagieren (und ich bin ein langjähriger C # -Programmierer und immer noch ein sehr großer Fan der Sprache). An dieser Syntax ist nichts Schreckliches. Es ist lediglich ein Versuch, die Syntax so zu gestalten, wie Sie es ausdrücken möchten. Je weniger "Rauschen" in der Syntax für etwas vorhanden ist, desto leichter kann der Programmierer es verstehen. Das Verringern des Rauschens in einer Codezeile hilft nur wenig, lässt dies jedoch über immer mehr Code hinweg aufbauen, und es stellt sich als wesentlicher Vorteil heraus.

Dies ist ein Versuch des Autors, nach den gleichen Vorteilen zu streben, die DSL Ihnen bietet - wenn der Code nur so aussieht, wie Sie es sagen möchten, haben Sie einen magischen Ort erreicht. Sie können darüber diskutieren, ob dies für Interop gut ist oder ob es besser als anonyme Methoden ist, um einen Teil der "Komplexitäts" -Kosten zu rechtfertigen. Fair genug ... also sollten Sie in Ihrem Projekt die richtige Wahl treffen, ob Sie diese Art von Syntax verwenden möchten. Aber dennoch ... dies ist ein kluger Versuch eines Programmierers, das zu tun, was wir letztendlich alle versuchen (ob wir es realisieren oder nicht). Und was wir alle versuchen, ist Folgendes: "Sagen Sie dem Computer, was er in einer Sprache tun soll, die so nah wie möglich an unserer Meinung darüber ist, was er tun soll."

Die Annäherung unserer Anweisungen an Computer auf dieselbe Weise, wie wir es intern denken, ist ein Schlüssel, um Software wartbarer und genauer zu machen.

EDIT: Ich hatte gesagt, "der Schlüssel, um Software wartbarer und genauer zu machen", was ein verrückt naives, überbewertetes Stück Einhorn ist. Ich habe es in "einen Schlüssel" geändert.

Charlie Flowers
quelle
12

Dies ist einer der Vorteile von Ausdrucksbäumen - man kann den Code selbst auf zusätzliche Informationen untersuchen. Auf diese Weise .Where(e => e.Name == "Jamie")kann in die entsprechende SQL Where-Klausel konvertiert werden. Dies ist eine clevere Verwendung von Ausdrucksbäumen, obwohl ich hoffen würde, dass es nicht weiter geht. Alles, was komplexer ist, ist wahrscheinlich schwieriger als der Code, den es ersetzen möchte, daher vermute ich, dass es selbstlimitierend sein wird.

Jamie Penney
quelle
Ist ein gültiger Punkt, aber die Wahrheit in der Werbung: LINQ enthält eine ganze Reihe von Attributen wie TableAttribute und ColumnAttribute, die dies zu einer legitimeren Angelegenheit machen. Bei der Linq-Zuordnung werden auch Klassennamen und Eigenschaftsnamen betrachtet, die möglicherweise stabiler sind als Parameternamen.
Remus Rusanu
Da stimme ich dir zu. Ich habe meine Haltung dazu leicht geändert, nachdem ich gelesen habe, was Eric Lippert / Anders Helsberg / etc zu diesem Thema zu sagen hatten. Ich dachte, ich würde diese Antwort offen lassen, da sie immer noch etwas hilfreich ist. Für das, was es wert ist, finde ich diese Art der Arbeit mit HTML jetzt nett, aber sie passt nicht zur Sprache.
Jamie Penney
7

Es ist ein interessanter Ansatz. Wenn Sie die rechte Seite des Ausdrucks auf Konstanten beschränkt haben, können Sie using implementieren

Expression<Func<object, string>>

Was ich denke, ist das, was Sie wirklich wollen, anstelle des Delegierten (Sie verwenden das Lambda, um die Namen beider Seiten zu erhalten). Siehe naive Implementierung unten:

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;
}

Dies könnte sogar das sprachübergreifende Interop-Problem angehen, das zuvor im Thread erwähnt wurde.

dfowler
quelle
6

Der Code ist sehr clever, verursacht jedoch möglicherweise mehr Probleme, die er löst.

Wie Sie bereits betont haben, besteht jetzt eine unklare Abhängigkeit zwischen dem Parameternamen (Stil) und einem HTML-Attribut. Es wird keine Überprüfung der Kompilierungszeit durchgeführt. Wenn der Parametername falsch eingegeben wird, wird auf der Seite wahrscheinlich keine Laufzeitfehlermeldung angezeigt, aber es ist viel schwieriger, einen Logikfehler zu finden (kein Fehler, aber falsches Verhalten).

Eine bessere Lösung wäre, ein Datenelement zu haben, das zur Kompilierungszeit überprüft werden kann. Also stattdessen:

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

Code mit einer Style-Eigenschaft kann vom Compiler überprüft werden:

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

oder auch:

.Attributes.Style.Width.Percent = 100;

Das ist mehr Arbeit für die Autoren des Codes, aber dieser Ansatz nutzt die starke Fähigkeit von C # zur Typprüfung, die verhindert, dass Fehler überhaupt in den Code gelangen.

Chris R. Timmons
quelle
3
Ich schätze die Überprüfung während der Kompilierung, aber ich denke, dies ist eine Ansichtssache. Vielleicht würde so etwas wie new Attributes () {Style: "width: 100%"} mehr Leute davon überzeugen, da es knapper ist. Trotzdem ist die Implementierung von allem, was HTML erlaubt, eine große Aufgabe, und ich kann niemandem die Schuld geben, nur Strings / Lambdas / anonyme Klassen zu verwenden.
Samantha Branham
5

in der Tat scheint es Ruby =) zu sein, zumindest für mich passt die Verwendung einer statischen Ressource für eine spätere dynamische "Suche" nicht zu Überlegungen zum API-Design. Ich hoffe, dieser clevere Trick ist in dieser API optional.

Wir könnten von IDictionary erben (oder nicht) und einen Indexer bereitstellen, der sich wie ein PHP-Array verhält, wenn Sie keinen Schlüssel hinzufügen müssen, um einen Wert festzulegen. Es wird eine gültige Verwendung der .net-Semantik sein, nicht nur von c #, und es wird noch Dokumentation benötigt.

hoffe das hilft

Horacio N. Hdez.
quelle
4

Meiner Meinung nach ist es Missbrauch der Lambdas.

In Bezug auf die Syntaxbrillanz finde ich das style=>"width:100%"einfach verwirrend. Besonders wegen der =>statt=

mfeingold
quelle
4

IMHO, es ist eine coole Art, es zu tun. Wir alle lieben die Tatsache, dass die Benennung einer Klasse Controller sie zu einer Steuerung in MVC macht, oder? Es gibt also Fälle, in denen die Benennung eine Rolle spielt.

Auch hier ist die Absicht sehr klar. Es ist sehr leicht zu verstehen, dass .Attribute( book => "something")dies zu book="something"und führen .Attribute( log => "something")wirdlog="something"

Ich denke, es sollte kein Problem sein, wenn Sie es wie eine Konvention behandeln. Ich bin der Meinung, dass alles, was Sie dazu bringt, weniger Code zu schreiben und die Absicht offensichtlich zu machen, eine gute Sache ist.

Madaboutcode
quelle
4
Das Benennen einer Klasse Controller wird jedoch nicht in die Hocke gehen, wenn Sie nicht auch von Controller erben ...
Jordan Wallwork
3

Wenn die Methodennamen (func) gut ausgewählt sind, ist dies eine hervorragende Möglichkeit, um Wartungsprobleme zu vermeiden (z. B. Hinzufügen einer neuen Funktion, aber vergessen, sie der Funktionsparameter-Zuordnungsliste hinzuzufügen). Natürlich müssen Sie es stark dokumentieren und Sie sollten die Dokumentation für die Parameter besser automatisch aus der Dokumentation für die Funktionen in dieser Klasse generieren ...

Craig Trader
quelle
1

Ich denke, das ist nicht besser als "magische Saiten". Auch dafür bin ich kein großer Fan der anonymen Typen. Es braucht einen besseren und stark typisierten Ansatz.

JamesK
quelle