Voglio prendere una classe, scorrere le sue proprietà, ottenere il valore della proprietà e chiamare un metodo che trasmetta il valore della proprietà. Penso di poter ottenere i valori della proprietà, ma come appare il corpo dell'espressione lambda? Quale corpo è usato per chiamare un metodo su ogni proprietà?
Questo è quello che ho finora ...
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();
}
Dipende da alcune cose.
il metodo restituisce qualcosa? Expression
in 3.5 non può eseguire più operazioni di "azione" separate (un corpo di istruzioni), ma puoi imbrogliare se puoi fare qualcosa con un'API fluente:
SomeMethod(obj.Prop1).SomeMethod(obj.Prop2).SomeMethod(obj.Prop3);
(magari usando i generici per renderlo più semplice)
hai accesso a 4.0? Nella versione 4.0 ci sono altri tipi di Expression
consentono agli enti di certificazione e esattamente ciò che chiedi. Discuto alcuni esempi simili in un articolo qui (cercate Expression.Block
, anche se questo è basato su una beta qualche tempo fa - potrebbe essere stato rinominato ora).
Alternativa; dal momento che si sta compilando a un delegato, si consideri che Action<T>
è multicast; potresti creare una serie di operazioni semplici e combinarle nel delegato; questo funzionerebbe in 3,5; per esempio:
using System;
using System.Linq.Expressions;
static class SomeType
{
static void SomeMethod<T>(T value)
{
Console.WriteLine(value);
}
}
class Customer
{
public int Id { get; set; }
public string Name { get; set; }
}
static class Program
{
static readonly Action<Customer> action = CreateAction<Customer>();
static void Main()
{
Customer cust = new Customer { Id = 123, Name = "Abc" };
action(cust);
}
static Action<T> CreateAction<T>()
{
Action<T> result = null;
var param = Expression.Parameter(typeof(T), "obj");
foreach (var property in typeof(T).GetProperties(
BindingFlags.Instance | BindingFlags.Public))
{
if (property.GetIndexParameters().Length > 0) continue;
var propVal = Expression.Property(param, property);
var call = Expression.Call(typeof(SomeType), "SomeMethod", new Type[] {propVal.Type}, propVal);
result += Expression.Lambda<Action<T>>(call, param).Compile();
}
return result;
}
}
Non penso che sarà così facile usare Expressions, in .NET 3.5 almeno.
.NET 4 supporta un costrutto di blocco, credo.
Suggerisco piuttosto di usare Reflection.Emit.
Ecco un punto di partenza (per i campi ma può essere modificato facilmente):
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);
}