Ausdruck zum Aufruf einer Methode für jede Eigenschaft einer Klasse

c# expression-trees lambda

Frage

Ich möchte eine Klasse durchlaufen, die Eigenschaften durchlaufen, den Eigenschaftswert abrufen und eine Methode aufrufen, die diesen Eigenschaftswert übergibt. Ich denke, ich kann die Eigenschaftswerte abrufen, aber wie sieht der Körper des Lambda-Ausdrucks aus? Mit welchem ​​Body wird eine Methode für jede Eigenschaft aufgerufen?

Das habe ich bisher ...

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();
}

Akzeptierte Antwort

Es hängt von ein paar Dingen ab.

  • Gibt die Methode irgendetwas zurück? Expression in 3.5 kann nicht mehrere separate "Aktions" -Operationen ausführen (ein Anweisungskörper), aber Sie können schummeln, wenn Sie etwas mit einer flüssigen API tun können:

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

    (vielleicht mit Generika, um es einfacher zu machen)

  • hast du Zugang zu 4.0? In 4.0 gibt es zusätzliche Expression , die Anweisungskörper erlauben und genau das , wonach Sie fragen. Ich bespreche einige ähnliche Beispiele in einem Artikel hier (suchen Sie nach Expression.Block , obwohl dies vor einiger Zeit auf einer Beta basiert - es könnte inzwischen umbenannt worden sein).

Alternative; Denken Sie daran, dass eine Action<T> Multicast ist, da Sie zu einem Delegaten kompilieren; Sie könnten eine Reihe von einfachen Operationen erstellen und diese im Delegaten kombinieren. das würde in 3.5 funktionieren; beispielsweise:

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

Beliebte Antwort

Ich denke nicht, dass es so einfach sein wird, Ausdrücke zu verwenden, zumindest in .NET 3.5.

.NET 4 unterstützt ein Blockkonstrukt, glaube ich.

Ich schlage vor, Reflection.Emit eher zu verwenden.

Hier ist ein Startpunkt (für Felder aber kann leicht geändert werden):

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);
}



Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum