Il est relativement facile de créer une fonction Lambda qui retournera la valeur d'une propriété à partir d'un objet, y compris des propriétés profondes ... et cela peut être appelé comme suit. .. p> mais, donné em> strud> la fonction résultante ci-dessus (ou l'expression initialement utilisée pour créer la fonction) , peut-on fournir un moyen facile de créer l'action opposée ... p> ... qui permettra la définition de la même valeur de propriété comme suit? P> Notez que je Je cherche un moyen de créer l'action programmatisée à partir de la fonction ou de l'expression - j'espère que j'ai montré que je savais déjà comment le créer manuellement. P> Je suis ouvert aux réponses qui fonctionnent dans les deux .net 3.5 et 4.0. P> merci. P> mise à jour: p> Peut-être que je ne suis peut-être pas clair dans ma question, alors laissez-moi essayer de démontrer plus clairement ce que je suis essayer faire. P> J'ai la méthode suivante (que j'ai créée pour les besoins de cette question) ... p> Action
Category category = *<get object from somewhere>*;
this.DoLambdaStuff(category, c => c.Name);
4 Réponses :
Cela devrait être possible en utilisant arbres d'expression , qui peut être créé à partir d'expressions Lambda, modifiées, puis compilées dans un délégué. P>
C'est le genre de chose que je cherchais, j'espérais juste un exemple!
OK, le code que je cherche va faire quelque chose comme ça ... Ce code fonctionne, mais uniquement pour les propriétés peu profondes (c'est-à-dire des propriétés de l'objet immédiat) mais Ne fonctionne pas pour des propriétés profondes - bien que la fonction getter puisse fonctionner pour des propriétés profondes. Il serait intéressant de savoir si quelqu'un peut m'aider à modifier cela pour travailler avec des propriétés profondes, mais je vais soulever cela comme une question séparée. P> P>
Ce code ne fonctionne que dans .NET 4 car il nécessite l'amélioration de la prise en charge des arbres d'expression introduits dans .NET 4.
Comme Martin notait ci-dessus, les propriétés "Deep" brisent sa solution. La raison pour laquelle il ne fonctionne pas est cette expression: La raison de celle-ci n'est pas immédiatement évidente: la classe dérivée déclare sa propre propriété Getter, qui redirige vers la classe de base, mais ne fait pas Définissez un seigter correspondant. P> Pour contourner ce problème, vous devez localiser la propriété par réflexion, rechercher son type de déclaration, puis obtenir la propriété correspondante du déclarant. Contrairement à la propriété que vous obtenez sur la classe dérivée, la propriété du déclarant a un getter et un setter, et est donc assignable. (Cela suppose qu'une sécheuse de la propriété est déclarée du tout: si le déclarant ne fournit pas de seigur, personne d'autre ne pourrait la fournir.) P> Voici une implémentation squelettique qui répond à ce problème. Remplacez l'appel ci-dessus à expression.property code> avec l'appel de
getpropertyorfield code> ci-dessous et que la solution de Martin va fonctionner. P>
private static MemberExpression GetPropertyOrField(Expression baseExpr, string name) {
if (baseExpr == null) {
throw new ArgumentNullException("baseExpr");
}
if (string.IsNullOrWhiteSpace(name)) {
throw new ArgumentException("name");
}
var type = baseExpr.Type;
var properties = type
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.Name.Equals(name))
.ToArray();
if (properties.Length == 1) {
var res = properties[0];
if (res.DeclaringType != type) {
// Here is the core of the fix:
var tmp = res
.DeclaringType
.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.Name.Equals(name))
.ToArray();
if (tmp.Length == 1) {
return Expression.Property(baseExpr, tmp[0]);
}
}
return Expression.Property(baseExpr, res);
}
if (properties.Length != 0) {
throw new NotSupportedException(name);
}
var fields = type
.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)
.Where(p => p.Name.Equals(name))
.ToArray();
if (fields.Length == 1) {
return Expression.Field(baseExpr, fields[0]);
}
if (fields.Length != 0) {
throw new NotSupportedException(name);
}
throw new ArgumentException(
string.Format(
"Type [{0}] does not define property/field called [{1}]"
, type
, name
)
);
}
void DoLambdaStuff<TObject, TValue>(TObject obj, Expression<Func<TObject, TValue>> expression) { Func<TObject, TValue> getValue = expression.Compile(); TValue stuff = getValue(obj); var p = Expression.Parameter(typeof(TValue), "v"); Expression<Action<TObject, TValue>> assignmentExpression = Expression.Lambda<Action<TObject, TValue>>(Expression.Assign(expression.Body, p), expression.Parameters[0], p); Action<TObject, TValue> setValue = assignmentExpression.Compile(); setValue(obj, stuff); }
+1. Utilisation de ce code, je suis capable de définir des valeurs pour champs privés code> et
propriétés privées imbriquées code> aussi. Le type de champ / propriété pourrait être un
struct code> aussi et cela fonctionne simplement. Aimerait vraiment une explication de la façon dont cela fonctionne.
Je ne comprends pas vraiment ce que vous entendez en le faisant automatiquement de manière manuelle. Toutefois, si vous souhaitez définir des propriétés et décider de la propriété que vous souhaitez définir au moment de l'exécution, vous devez utiliser la réflexion. De plus, vous pouvez utiliser l'arborescence d'expression pour construire des expressions Lambda à temps d'exécution.