Ich habe eine Methode, die ich in Extension-Methode
konvertieren möchte %Vor%und es wie
aufrufen %Vor%, sodass es zu str = "Bar"; // It gives the Member name and not its value
Wenn ich jetzt versuche, dies in die Erweiterungsmethode umzuwandeln
%Vor%und nenne es wie
%Vor% Fehler sagt Operator '.' cannot be applied to operand of type 'lambda expression'
Wo liege ich falsch?
Es gibt wirklich zwei Dinge hier, erstens, () => new Foo().Bar
an die Methode übergeben, die Expression<Func<T>>
akzeptiert, behandelt die angegebene Ausdrucksbaumstruktur als Expression<Func<T>>
, aber () => new Foo().Bar
ist keine Expression<Func<T>>
allein.
Zweitens, damit Ihre Erweiterungsmethode Lambda akzeptiert (wie Sie es liefern), müssten Sie den Typ verwenden, der einer beliebigen Ausdrucksbaumstruktur entspricht. Aber wie Sie vielleicht bereits anhand der Nachricht ... to operand of type 'lambda expression'
erraten haben, wo Sie normalerweise den Namen des Typs in den Anführungszeichen sehen würden, werden diese Lambda-Ausdrücke speziell von der Sprache behandelt und machen das, was Sie zu tun versuchen, ohne Casting zuerst, unmöglich.
Der Weg, Ihre Erweiterungsmethode in der Erweiterungsmethode aufzurufen, wäre (falls Bar
vom Typ string
ist)
was nicht so aussieht, als wäre es alles wünschenswert.
Wo liege ich falsch?
Der Compiler sagt Ihnen genau, was falsch ist - Sie können .
nicht für einen Lambda-Ausdruck verwenden.
Der Lambda-Ausdruck hat keinen bestimmten Typ - er ist nur konvertierbar in den Ausdrucksbaum.
DerA -Elementzugriff -Ausdruck (was Sie gerade versuchen) ist nur in den Formularen
verfügbar %Vor%... und ein Lambda-Ausdruck ist kein primärer Ausdruck.
Interessanterweise enthält dieses Argument nicht für einen anonymen -Methoden -Ausdruck, aber Sie können auch weiterhin keinen Mitgliedszugriffsausdruck verwenden. Abschnitt 7.6.4 der C # -Spezifikation listet auf, wie ein Mitgliedszugriffsausdruck gebunden ist, und der Großteil der Optionen befindet sich entweder unter "Wenn E ein vordefinierter Typ ist oder ein primärer Ausdruck klassifiziert als Typ "(was nicht für anonyme Methoden gilt) oder" Wenn E ein Eigenschaftenzugriff, eine Variable oder ein Wert ist, dessen Typ T ist "- aber eine anonyme Methode ist eine anonyme Funktion, und wie in Abschnitt 7.15: "Eine anonyme Funktion hat keinen Wert und gibt nicht selbst ein".
BEARBEITEN: Sie können noch Erweiterungsmethoden für Ausdrucksbäume verwenden, Sie können sie nicht direkt in Lambda-Ausdrücken verwenden. Also wird das funktionieren:
%Vor%... aber es ist offensichtlich nicht so nützlich. (Dito mit einer Besetzung wie von mlorbetske's Antwort.)
Um einen getippten Ausdruck zu erhalten, müssen Sie ihn ausschreiben. Wie andere gesagt haben, gibt es keinen Weg, wie der Compiler automatisch von einem Lambda-Ausdruck auf einen Lambda-Ausdruck schließen kann, da ein Lambda-Ausdruck zwei Dinge bedeuten kann - entweder einen Delegaten oder einen Ausdrucksbaum.
Sie können den Ausdruck relativ einfacher erhalten, indem Sie den Compiler für Sie teilweise den Typ ableiten lassen, wie ( aus dieser Antwort ):
%Vor%Nennen Sie es wie folgt:
%Vor%Ihre Optionen sind:
Wirf zurück, um den Ausdruckstyp zu bestimmen und rufe dann die Erweiterungsmethode auf
%Vor%sieht hässlich aus - Heilung schlimmer als Ursache.
Holen Sie den Ausdruck zuerst in eine Variable
%Vor%Etwas besser, aber sieht immer noch ein wenig trivial aus.
Verwenden der oben angegebenen Lambda
-Klassen
Noch besser. Es ist nur ein bisschen weniger tippen.
Dein erstes Muster, von dem ich denke, dass es das Beste ist, was du bekommen kannst, bis hin zur Lesbarkeit
Ich denke nicht, dass Sie das verbessern können, wenn Sie C # -Regeln in Bezug auf Lambda-Ausdrücke betrachten. Das sagte ich denke, dass einige Verbesserungen vorgenommen werden können.
Erweitern Sie zuerst die Funktionalität auf andere Lambda-Ausdruckstypen. Sie benötigen mehr als Func<T>
types, um alle Fälle zu bearbeiten. Entscheiden Sie, welche Ausdruckstypen Sie behandeln müssen. Zum Beispiel, wenn Sie einen Func<S, T>
-Typ haben (wie in Ihrer Frage - Bar
auf Foo
), sieht es besser aus. Vergleichen Sie dies
mit
%Vor%Ich würde sagen, diese Überladungen würden tun:
%Vor% Nun können diese nicht nur in den in Kommentaren erwähnten Fällen verwendet werden, sicherlich gibt es überlappende Szenarien. Wenn Sie beispielsweise eine Instanz von Foo
haben, ist es einfacher, die zweite Überladung ( Func<T>
) für den Namen der Eigenschaft Bar
, wie myclass.GetMemberName(() => foo.Bar)
, aufzurufen.
Zweitens implementieren Sie eine GetMemberName
-Funktionalität, die allen diesen Überladungen gemeinsam ist. Eine Erweiterungsmethode für Expression<T>
oder LambdaExpression
würde ausreichen. Ich bevorzuge Letzteres, so dass Sie es sogar in nicht stark typisierten Szenarien nennen können. Ich würde es so schreiben, aus dieser Antwort :
Schließlich ist GetMemberName
kein guter Name, wenn Sie es nicht-extension-weise nennen wollen. expression.GetMemberName()
klingt logischer. Member.NameFrom<int>(x => x.ToString())
oder MemberName.From<string>(x => x.Length)
etc sind aussagekräftigere Namen für statische Aufrufe.
Insgesamt könnte die Klasse so aussehen:
%Vor%Und Verwendung:
%Vor%Am besten prägnant und sauber.
Für Eigenschaften und Felder kann es in eine anonyme Klasse umgewandelt werden, und dann kann der Name des Mitglieds durch Reflektion gelesen werden, wie hier gezeigt.
%Vor%Nennen Sie es wie
%Vor%Es ist schneller, aber hat gewisse Macken, wie nicht sehr Refactor freundlich, und hilft nicht im Falle von Methoden als Mitglieder. Siehe den Thread ..
Von allen bevorzuge ich 4.
Tags und Links .net c# linq extension-methods