Expression pour appeler une méthode sur chaque propriété d'une classe

c# expression-trees lambda

Question

Je souhaite prendre une classe, parcourir ses propriétés, obtenir la valeur de la propriété et appeler une méthode transmettant cette valeur de propriété. Je pense pouvoir obtenir les valeurs de la propriété, mais à quoi ressemble le corps de l'expression lambda? Quel corps est utilisé pour appeler une méthode sur chaque propriété?

C'est ce que j'ai jusqu'ici ...

Action<T> CreateExpression<T>( T obj )
{
 foreach( var property in typeof( T ).GetProperties() )
 {
  Expression value = Expression.Property( Expression.Constant( obj ), property );
  var method = Expression.Call( typeof( SomeType ), "SomeMethod", null, value );
 }

 // What expression body can be used that will call
 // all the method expressions for each property?
 var body = Expression...
 return Expression.Lambda<Action<T>>( body, ... ).Compile();
}

Réponse acceptée

Cela dépend de quelques choses.

  • la méthode retourne-t-elle quelque chose? Expression dans 3.5 ne peut pas effectuer plusieurs opérations "action" distinctes (un corps d'instruction), mais vous pouvez tricher si vous pouvez faire quelque chose avec une API fluide:

    SomeMethod(obj.Prop1).SomeMethod(obj.Prop2).SomeMethod(obj.Prop3);
    

    (peut-être en utilisant des génériques pour simplifier)

  • Avez-vous accès à la version 4.0? Dans la version 4.0, il existe des types d' Expression supplémentaires permettant aux corps d'instructions et exactement ce que vous demandez. Je discute d’exemples similaires dans un article ici (recherchez Expression.Block , bien qu’il soit basé sur une version bêta il y a quelque temps - il a peut-être été renommé maintenant).

Alternative; puisque vous compilez pour un délégué, considérez qu'une Action<T> est une multidiffusion; vous pouvez créer un ensemble d'opérations simples et les combiner dans le délégué; cela fonctionnerait dans 3.5; par exemple:

SomeMethod(obj.Prop1).SomeMethod(obj.Prop2).SomeMethod(obj.Prop3);

Réponse populaire

Je ne pense pas que ce sera si facile d’utiliser Expressions, du moins dans .NET 3.5.

.NET 4 prend en charge une construction de bloc, je crois.

Je suggère plutôt d'utiliser Reflection.Emit.

Voici un point de départ (pour les champs mais peut être modifié facilement):

internal static T CreateDelegate<T>(this DynamicMethod dm) where T : class
{
  return dm.CreateDelegate(typeof(T)) as T;
}

static Dictionary<Type, Func<object, Dictionary<string, object>>> fieldcache = 
  new Dictionary<Type, Func<object, Dictionary<string, object>>>();

static Dictionary<string, object> GetFields(object o)
{
  var t = o.GetType();

  Func<object, Dictionary<string, object>> getter;

  if (!fieldcache.TryGetValue(t, out getter))
  {
    var rettype = typeof(Dictionary<string, object>);

    var dm = new DynamicMethod(t.Name + ":GetFields", 
       rettype, new Type[] { typeof(object) }, t);

    var ilgen = dm.GetILGenerator();

    var instance = ilgen.DeclareLocal(t);
    var dict = ilgen.DeclareLocal(rettype);

    ilgen.Emit(OpCodes.Ldarg_0);
    ilgen.Emit(OpCodes.Castclass, t);
    ilgen.Emit(OpCodes.Stloc, instance);

    ilgen.Emit(OpCodes.Newobj, rettype.GetConstructor(Type.EmptyTypes));
    ilgen.Emit(OpCodes.Stloc, dict);

    var add = rettype.GetMethod("Add");

    foreach (var field in t.GetFields(
      BindingFlags.DeclaredOnly |
      BindingFlags.Instance |
      BindingFlags.Public |
      BindingFlags.NonPublic))
    {
      if (!field.FieldType.IsSubclassOf(typeof(Component)))
      {
        continue;
      }
      ilgen.Emit(OpCodes.Ldloc, dict);

      ilgen.Emit(OpCodes.Ldstr, field.Name);

      ilgen.Emit(OpCodes.Ldloc, instance);
      ilgen.Emit(OpCodes.Ldfld, field);
      ilgen.Emit(OpCodes.Castclass, typeof(object));

      ilgen.Emit(OpCodes.Callvirt, add);
    }

    ilgen.Emit(OpCodes.Ldloc, dict);
    ilgen.Emit(OpCodes.Ret);

    fieldcache[t] = getter = dm.CreateDelegate<Func<object, 
       Dictionary<string, object>>>();
  }

  return getter(o);
}



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi