Gibt es eine bessere Möglichkeit, den Eigenschaftsnamen zu erhalten, wenn er über einen Lambda-Ausdruck übergeben wird? Hier ist was ich derzeit habe.
z.B.
GetSortingInfo<User>(u => u.UserId);
Es funktionierte, indem es nur dann als Mitgliedsausdruck umgewandelt wurde, wenn die Eigenschaft eine Zeichenfolge war. Da nicht alle Eigenschaften Zeichenfolgen sind, musste ich ein Objekt verwenden, aber dann würde es einen unären Ausdruck für diese zurückgeben.
public static RouteValueDictionary GetInfo<T>(this HtmlHelper html,
Expression<Func<T, object>> action) where T : class
{
var expression = GetMemberInfo(action);
string name = expression.Member.Name;
return GetInfo(html, name);
}
private static MemberExpression GetMemberInfo(Expression method)
{
LambdaExpression lambda = method as LambdaExpression;
if (lambda == null)
throw new ArgumentNullException("method");
MemberExpression memberExpr = null;
if (lambda.Body.NodeType == ExpressionType.Convert)
{
memberExpr =
((UnaryExpression)lambda.Body).Operand as MemberExpression;
}
else if (lambda.Body.NodeType == ExpressionType.MemberAccess)
{
memberExpr = lambda.Body as MemberExpression;
}
if (memberExpr == null)
throw new ArgumentException("method");
return memberExpr;
}
c#
linq
lambda
expression-trees
Schotime
quelle
quelle
MemberExpression
hier aufgeführten Ansatz nur, um den Namen des Mitglieds abzurufen , nicht um den tatsächlichen NamenMemberInfo
selbstMemberInfo
abzurufen , da in bestimmten "dervied: base" -Szenarien nicht garantiert wird, dass der zurückgegebene Typ vom reflektierten Typ ist. Siehe Lambda-Ausdruck-nicht-Rückgabe-erwartet-Mitgliedinfo . Hat mich einmal gestolpert. Darunter leidet auch die akzeptierte Antwort.Antworten:
Ich habe kürzlich eine sehr ähnliche Aktion durchgeführt, um eine typsichere OnPropertyChanged-Methode zu erstellen.
Hier ist eine Methode, die das PropertyInfo-Objekt für den Ausdruck zurückgibt. Es wird eine Ausnahme ausgelöst, wenn der Ausdruck keine Eigenschaft ist.
Der
source
Parameter wird verwendet, damit der Compiler beim Methodenaufruf eine Typinferenz durchführen kann. Sie können Folgendes tunquelle
u => u.OtherType.OtherTypesProperty
würde einen solchen Fall erstellen, auf den die letzte Anweisung prüft.if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType) && !propInfo.ReflectedType.IsAssignableFrom(type))
um auch Schnittstellen zu berücksichtigen.if(!propInfo.ReflectedType.IsAssignableFrom(type))
?Ich habe eine andere Möglichkeit gefunden, die Quelle und die Eigenschaft stark zu tippen und die Eingabe für das Lambda explizit abzuleiten. Ich bin mir nicht sicher, ob dies die richtige Terminologie ist, aber hier ist das Ergebnis.
Und dann nenne es so.
und voila es funktioniert.
Vielen Dank an alle.
quelle
GetInfo(nameof(u.UserId))
var name = ((MemberExpression) ((UnaryExpression) accessor.Body).Operand).Member.Name
Ich habe mit der gleichen Sache herumgespielt und das aufgearbeitet. Es ist nicht vollständig getestet, scheint aber das Problem mit Werttypen zu behandeln (das Problem mit dem unären Ausdruck, auf das Sie gestoßen sind).
quelle
o => o.Thing1.Thing2
würde zurückkehrenThing2
, nichtThing1.Thing2
, was falsch ist, wenn Sie versuchen, es in EntityFramework-Includes zu verwendenDies behandelt Element- und unäre Ausdrücke. Der Unterschied besteht darin, dass Sie ein erhalten,
UnaryExpression
wenn Ihr Ausdruck einen Werttyp darstellt, während Sie ein erhalten,MemberExpression
wenn Ihr Ausdruck einen Referenztyp darstellt. Alles kann in ein Objekt umgewandelt werden, aber Werttypen müssen eingerahmt werden. Aus diesem Grund ist der UnaryExpression vorhanden. Referenz.Aus Gründen der Lesbarkeit (@Jowen) ist hier ein erweitertes Äquivalent:
quelle
Mit C # 7 Mustervergleich:
Beispiel:
[Update] C # 8-Mustervergleich:
quelle
Jetzt können Sie in C # 6 einfach den Namen so verwenden
nameof(User.UserId)
Dies hat viele Vorteile, darunter, dass dies zur Kompilierungszeit und nicht zur Laufzeit erfolgt.
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
quelle
Dies ist eine allgemeine Implementierung, um den Zeichenfolgennamen der Felder / Eigenschaften / Indexer / Methoden / Erweiterungsmethoden / Delegaten von struct / class / interface / delegate / array abzurufen. Ich habe mit Kombinationen von statischen / Instanz- und nicht generischen / generischen Varianten getestet.
Dieses Ding kann auch in einer einfachen
while
Schleife geschrieben werden:Ich mag den rekursiven Ansatz, obwohl der zweite möglicherweise leichter zu lesen ist. Man kann es so nennen:
um das letzte Mitglied zu drucken.
Hinweis:
Bei verketteten Ausdrücken wie
A.B.C
"C" wird zurückgegeben.Dies funktioniert nicht mit
const
s, Array-Indexern oderenum
s (es ist unmöglich, alle Fälle abzudecken).quelle
Es gibt einen
Array
Randfall, wenn es um .Length geht. Während 'Länge' als Eigenschaft verfügbar gemacht wird, können Sie es in keiner der zuvor vorgeschlagenen Lösungen verwenden.Jetzt Beispielverwendung:
Wenn
PropertyNameFromUnaryExpr
nicht nachArrayLength
"someArray" gesucht wird, wird auf der Konsole gedruckt (der Compiler scheint als Optimierung auch in Debug, also im Sonderfall , direkten Zugriff auf das Feld " Backing Length" zu generieren ).quelle
Hier ist ein Update der von Cameron vorgeschlagenen Methode . Der erste Parameter ist nicht erforderlich.
Sie können Folgendes tun:
Erweiterungsmethoden:
Du kannst:
quelle
u
als Typ schließen, das kann er nicht, weil es keinen Typ gibt, auf den man schließen kann. Was Sie tun können, istGetPropertyInfo<SomeType>(u => u.UserID)
Ich habe festgestellt, dass einige der vorgeschlagenen Antworten einen Drilldown in die
MemberExpression
/UnaryExpression
nicht erfassten verschachtelten / Untereigenschaften durchführen.ex)
o => o.Thing1.Thing2
kehrtThing1
eher zurück alsThing1.Thing2
.Diese Unterscheidung ist wichtig, wenn Sie versuchen, mit EntityFramework zu arbeiten
DbSet.Include(...)
.Ich habe festgestellt, dass nur das Parsen des
Expression.ToString()
zu funktionieren scheint, und vergleichsweise schnell. Ich verglich es mit derUnaryExpression
Version und stieg sogar aus,ToString
umMember/UnaryExpression
zu sehen, ob das schneller war, aber der Unterschied war vernachlässigbar. Bitte korrigieren Sie mich, wenn dies eine schreckliche Idee ist.Die Erweiterungsmethode
(Das Überprüfen des Trennzeichens ist möglicherweise sogar übertrieben.)
Demo (LinqPad)
Demonstrations- und Vergleichscode - https://gist.github.com/zaus/6992590
quelle
o => o.Thing1.Thing2
, kehrenThing1
Sie nicht zurück, wie Sie sagenThing2
. Tatsächlich gibt Ihre Antwort so etwas zurück, wie esThing1.Thing2
vielleicht erwünscht ist oder nicht.Thing1.Thing2
Thing1
Thing2
o.Thing1.Thing2
Thing1
? Ich glaube nicht, dass es das überhaupt wiederholt.Ich verwende eine Erweiterungsmethode für Projekte vor C # 6 und den Namen () für diejenigen, die auf C # 6 abzielen.
Und ich nenne es wie:
Es funktioniert gut mit Feldern und Eigenschaften.
quelle
Nun, es besteht keine Notwendigkeit anzurufen
.Name.ToString()
, aber im Großen und Ganzen geht es darum, ja. Die einzige Überlegung, die Sie möglicherweise benötigen, ist, obx.Foo.Bar
"Foo", "Bar" oder eine Ausnahme zurückgegeben werden soll - dh, Sie müssen überhaupt iterieren.Weitere Informationen zur flexiblen Sortierung finden Sie hier .
quelle
ToString
sollte hässliche Ergebnisse für unäre Ausdrücke geben.Ich habe eine Erweiterungsmethode für ObjectStateEntry erstellt, um Eigenschaften (von Entity Framework-POCO-Klassen) als typsicher ändern zu können, da die Standardmethode nur eine Zeichenfolge akzeptiert. Hier ist meine Art, den Namen von der Unterkunft zu erhalten:
quelle
Ich habe die
INotifyPropertyChanged
Implementierung ähnlich der folgenden Methode durchgeführt. Hier werden die Eigenschaften in einem Wörterbuch in der unten gezeigten Basisklasse gespeichert. Es ist natürlich nicht immer wünschenswert, die Vererbung zu verwenden, aber für Ansichtsmodelle halte ich dies für akzeptabel und gebe sehr saubere Eigenschaftsreferenzen in den Ansichtsmodellklassen.Die etwas komplexere Basisklasse ist unten dargestellt. Es behandelt die Übersetzung vom Lambda-Ausdruck zum Eigenschaftsnamen. Beachten Sie, dass die Eigenschaften wirklich Pseudo-Eigenschaften sind, da nur die Namen verwendet werden. Es erscheint jedoch für das Ansichtsmodell transparent und verweist auf die Eigenschaften des Ansichtsmodells.
quelle
public bool IsLoading { get { return GetValue(MethodBase.GetCurrentMethod().Name); } set { SetPropertyValue(MethodBase.GetCurrentMethod().Name, value); } }
. Könnte langsamer sein, aber allgemeiner und unkomplizierter.Dies ist eine weitere Antwort:
quelle
ModelMetadata
ist imSystem.Web.Mvc
Namespace vorhanden. Vielleicht ist es nicht für den allgemeinen FallIch verlasse diese Funktion, wenn Sie mehrere Felder erhalten möchten:
quelle
Hier ist eine andere Möglichkeit, die PropertyInfo anhand dieser Antwort zu ermitteln. Eine Objektinstanz ist nicht mehr erforderlich.
Es kann so genannt werden:
quelle
Ich habe die Antwort von @ Cameron aktualisiert , um einige Sicherheitsüberprüfungen gegen
Convert
typisierte Lambda-Ausdrücke aufzunehmen:quelle
Ab .NET 4.0 können
ExpressionVisitor
Sie folgende Eigenschaften suchen:So verwenden Sie diesen Besucher:
quelle
Dies könnte optimal sein
quelle
quelle