Existe-t-il un meilleur moyen d'obtenir le nom de la propriété lorsqu'il est transmis via une expression lambda? Voici ce que j'ai actuellement.
par exemple.
GetSortingInfo<User>(u => u.UserId);
Il a fonctionné en le convertissant en expression de membre uniquement lorsque la propriété était une chaîne. parce que toutes les propriétés ne sont pas des chaînes, j'ai dû utiliser un objet, mais cela retournerait une expression unaire pour celles-ci.
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
la source
la source
MemberExpression
approche répertoriée ici uniquement pour obtenir le nom du membre, pas pour obtenir le réelMemberInfo
lui-même, car leMemberInfo
retour n'est pas garanti comme étant du type reflété dans certains scénarios "dervied: base". Voir lambda-expression-not-retourner-attendu-memberinfo . M'a fait trébucher une fois. La réponse acceptée en souffre également.Réponses:
J'ai récemment fait une chose très similaire pour rendre une méthode OnPropertyChanged sécurisée de type.
Voici une méthode qui retournera l'objet PropertyInfo pour l'expression. Il lève une exception si l'expression n'est pas une propriété.
Le
source
paramètre est utilisé pour que le compilateur puisse faire une inférence de type sur l'appel de méthode. Vous pouvez effectuer les opérations suivantesla source
u => u.OtherType.OtherTypesProperty
créerait un tel cas que la dernière instruction recherche.if (type != propInfo.ReflectedType && !type.IsSubclassOf(propInfo.ReflectedType) && !propInfo.ReflectedType.IsAssignableFrom(type))
pour permettre également les interfaces.if(!propInfo.ReflectedType.IsAssignableFrom(type))
?J'ai trouvé une autre façon de le faire était d'avoir la source et la propriété fortement typées et de déduire explicitement l'entrée pour le lambda. Je ne sais pas si c'est la terminologie correcte, mais voici le résultat.
Et puis appelez-le comme ça.
et le tour est joué.
Merci a tous.
la source
GetInfo(nameof(u.UserId))
var name = ((MemberExpression) ((UnaryExpression) accessor.Body).Operand).Member.Name
Je jouais avec la même chose et ça a fonctionné. Il n'est pas entièrement testé mais semble gérer le problème avec les types de valeur (le problème d'expression unaire que vous avez rencontré)
la source
o => o.Thing1.Thing2
retourneraitThing2
, nonThing1.Thing2
, ce qui est incorrect si vous essayez de l'utiliser dans EntityFramework comprendCela gère les expressions membres et unaires. La différence étant que vous obtiendrez un
UnaryExpression
si votre expression représente un type de valeur tandis que vous obtiendrez unMemberExpression
si votre expression représente un type de référence. Tout peut être converti en objet, mais les types de valeur doivent être encadrés. C'est pourquoi l'UnaryExpression existe. Référence.Pour des raisons de lisibilité (@Jowen), voici un équivalent étendu:
la source
Avec la correspondance de motifs C # 7:
Exemple:
[Mise à jour] Correspondance de modèle C # 8:
la source
maintenant en C # 6, vous pouvez simplement utiliser nameof comme celui-ci
nameof(User.UserId)
ce qui présente de nombreux avantages, parmi eux, cela se fait au moment de la compilation , pas au moment de l'exécution.
https://msdn.microsoft.com/en-us/magazine/dn802602.aspx
la source
Il s'agit d'une implémentation générale pour obtenir le nom de chaîne des champs / propriétés / indexeurs / méthodes / méthodes d'extension / délégués de struct / classe / interface / délégué / tableau. J'ai testé avec des combinaisons de variantes statiques / instance et non génériques / génériques.
Cette chose peut également être écrite dans une
while
boucle simple :J'aime l'approche récursive, bien que la seconde soit plus facile à lire. On peut l'appeler comme:
pour imprimer le dernier membre.
Remarque:
Dans le cas d'expressions chaînées comme
A.B.C
, "C" est retourné.Cela ne fonctionne pas avec
const
s, indexeurs de tableaux ouenum
s (impossible de couvrir tous les cas).la source
Il y a un cas de bord en ce qui concerne la
Array
longueur. Bien que la «longueur» soit exposée en tant que propriété, vous ne pouvez pas l'utiliser dans aucune des solutions proposées précédemment.Maintenant, exemple d'utilisation:
S'il
PropertyNameFromUnaryExpr
ne vérifiait pasArrayLength
, "someArray" serait imprimé sur la console (le compilateur semble générer un accès direct au champ Backing Length , en tant qu'optimisation, même dans Debug, donc le cas spécial).la source
Voici une mise à jour de la méthode proposée par Cameron . Le premier paramètre n'est pas requis.
Vous pouvez effectuer les opérations suivantes:
Méthodes d'extension:
Vous pouvez:
la source
u
comme un type, il ne peut pas le faire parce qu'il n'y a pas de type à déduire. Ce que vous pouvez faire estGetPropertyInfo<SomeType>(u => u.UserID)
J'ai trouvé que certaines des réponses suggérées qui forent vers le bas dans la
MemberExpression
/UnaryExpression
ne capture pas imbriquées / sous -propriétés .ex)
o => o.Thing1.Thing2
renvoieThing1
plutôt queThing1.Thing2
.Cette distinction est importante si vous essayez de travailler avec EntityFramework
DbSet.Include(...)
.J'ai trouvé que l'analyse syntaxique de
Expression.ToString()
semble fonctionner correctement et relativement rapidement. Je l'ai comparé à laUnaryExpression
version, et même enToString
descendantMember/UnaryExpression
pour voir si c'était plus rapide, mais la différence était négligeable. Veuillez me corriger si c'est une terrible idée.La méthode d'extension
(La vérification du délimiteur peut même être exagérée)
Démo (LinqPad)
Démonstration + code de comparaison - https://gist.github.com/zaus/6992590
la source
o => o.Thing1.Thing2
ne revenez pasThing1
comme vous le dites, maisThing2
. En fait, votre réponse renvoie quelque chose commeThing1.Thing2
ce qui peut ou non être souhaité.Thing1.Thing2
, jamaisThing1
. J'ai ditThing2
signifiant la valeur deo.Thing1.Thing2
, qui est le point du prédicat. Je mettrai à jour la réponse pour refléter cette intention.Thing1
? Je ne pense pas que cela revienne du tout.J'utilise une méthode d'extension pour les projets pré C # 6 et le nom de () pour ceux qui ciblent C # 6.
Et je l'appelle comme:
Cela fonctionne très bien avec les champs et les propriétés.
la source
Eh bien, il n'est pas nécessaire d'appeler
.Name.ToString()
, mais en gros c'est à peu près tout, oui. La seule considération dont vous pourriez avoir besoin est de savoir si vous devezx.Foo.Bar
renvoyer "Foo", "Bar" ou une exception - c.-à-d. Avez-vous besoin d'itérer du tout.(re comment) pour plus d'informations sur le tri flexible, voir ici .
la source
ToString
devrait donner des résultats moches pour les expressions unaires.J'ai créé une méthode d'extension sur ObjectStateEntry pour pouvoir marquer les propriétés (des classes Entity Framework POCO) comme modifiées de manière sécurisée, car la méthode par défaut n'accepte qu'une chaîne. Voici ma façon d'obtenir le nom de la propriété:
la source
J'ai fait l'
INotifyPropertyChanged
implémentation similaire à la méthode ci-dessous. Ici, les propriétés sont stockées dans un dictionnaire dans la classe de base indiquée ci-dessous. Il n'est bien sûr pas toujours souhaitable d'utiliser l'héritage, mais pour les modèles de vue, je pense qu'il est acceptable et donne des références de propriété très propres dans les classes de modèles de vue.La classe de base un peu plus complexe est illustrée ci-dessous. Il gère la traduction de l'expression lambda en nom de propriété. Notez que les propriétés sont vraiment des pseudo-propriétés puisque seuls les noms sont utilisés. Mais il apparaîtra transparent pour le modèle de vue et les références aux propriétés sur le modèle de vue.
la source
public bool IsLoading { get { return GetValue(MethodBase.GetCurrentMethod().Name); } set { SetPropertyValue(MethodBase.GetCurrentMethod().Name, value); } }
. Pourrait être plus lent, mais plus générique et simple.Voici une autre réponse:
la source
ModelMetadata
existe dans l'System.Web.Mvc
espace de noms. Peut-être que ce n'est pas adapté au cas généralJe laisse cette fonction si vous souhaitez obtenir plusieurs champs:
la source
Voici une autre façon d'obtenir le PropertyInfo basé sur cette réponse. Il élimine le besoin d'une instance d'objet.
Il peut être appelé ainsi:
la source
J'ai mis à jour la réponse de @ Cameron pour inclure des contrôles de sécurité par rapport aux
Convert
expressions lambda typées:la source
À partir de .NET 4.0, vous pouvez utiliser
ExpressionVisitor
pour rechercher des propriétés:Voici comment vous utilisez ce visiteur:
la source
Cela pourrait être optimal
la source
la source