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?
quelle
Antworten:
Dies hat eine schlechte Interop. Betrachten Sie beispielsweise dieses C # - F # -Beispiel
C #:
F #:
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.
quelle
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:
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).
quelle
HtmlAttributes
Klasse mit den erwarteten Attributen (für Intellisense) haben und diese mitnull
Werten einfach ignorieren ...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 ).
quelle
ich würde bevorzugen
Es ist viel expliziter und standardisierter und durch die Verwendung von Lambdas wird nichts erreicht.
quelle
html.Attributes.Add("style", "width:100%");
liest sich nicht so gut wiestyle = "width:100%"
(das tatsächlich erzeugte HTML), wohingegenstyle => "width:100%"
sehr nahe daran ist, wie es im resultierenden HTML aussieht.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.
quelle
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.
quelle
Ich bin im Lager "Syntax Brilliance", wenn sie es klar dokumentieren und es so verdammt cool aussieht, gibt es imo fast kein Problem damit!
quelle
Beide. Es ist der Missbrauch von Lambda-Ausdrücken UND Syntax-Brillanz.
quelle
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
Vorteile
Fazit : Im öffentlichen API-Design hätte ich einen expliziteren Weg gewählt.
quelle
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:
Obwohl dies etwas mehr zu tippen ist, ist es klar und intuitiv.
quelle
.Attribute("style", "width:100%")
gibt gibt mirstyle="width:100%"
, aber soweit ich weiß, könnte es mir gebenfoooooo
. Ich sehe den Unterschied nicht.Kann ich damit eine Phrase prägen?
magisches Lambda (n): Eine Lambda-Funktion, die ausschließlich zum Ersetzen einer magischen Zeichenfolge verwendet wird.
quelle
Was ist los mit folgendem:
quelle
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.
quelle
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.quelle
Es ist ein interessanter Ansatz. Wenn Sie die rechte Seite des Ausdrucks auf Konstanten beschränkt haben, können Sie using implementieren
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:
Dies könnte sogar das sprachübergreifende Interop-Problem angehen, das zuvor im Thread erwähnt wurde.
quelle
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:
Code mit einer Style-Eigenschaft kann vom Compiler überprüft werden:
oder auch:
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.
quelle
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
quelle
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=
quelle
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 zubook="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.
quelle
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 ...
quelle
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.
quelle