我正在尝试创建一个通用函数,它将采用一个结构的 2 个实例,并使用传入实例的值创建一个新实例。我大部分时间都在那里,但我无法弄清楚如何构建表达式树以将新值作为 MemberInit 中的参数(第一次使用表达式树)。
我尽量避免产生垃圾(所以没有装箱)。
这是我目前所拥有的:
private static readonly Dictionary<Type, FieldInfo[]> fieldInfoCache = new Dictionary<Type, FieldInfo[]>();
private static readonly Dictionary<FieldInfo, dynamic> compiledDelegates = new Dictionary<FieldInfo, dynamic>();
private static T Lerp<T>(T start, T end, float amount) where T : new()
{
FieldInfo[] fields;
var type = typeof(T);
if(!fieldInfoCache.TryGetValue(type, out fields))
{
fields = typeof(T).GetFields(BindingFlags.Public | BindingFlags.Instance);
fieldInfoCache.Add(type, fields);
}
var binds = new List<MemberBinding>();
foreach(var fieldInfo in fields)
{
dynamic getter;
if(!compiledDelegates.TryGetValue(fieldInfo, out getter))
{
var targetExp = Expression.Parameter(type, type.Name);
var fieldExp = Expression.Field(targetExp, fieldInfo);
getter = Expression.Lambda(typeof(Func<,>).MakeGenericType(type, fieldInfo.FieldType), fieldExp, targetExp).Compile();
compiledDelegates.Add(fieldInfo, getter);
}
var startVal = getter.Invoke(start);
var endVal = getter.Invoke(end);
//This needs to be assigned to something
var newVal = fieldInfo.FieldType.IsAssignableFrom(typeof(float)) ? LerpF(startVal, endVal, amount) : Lerp(startVal, endVal, amount);
var fieldParamExp = Expression.Parameter(fieldInfo.FieldType, "newVal");
var bind = Expression.Bind(fieldInfo, fieldParamExp);
binds.Add(bind);
}
//How do I fix these two lines?
var memberInit = Expression.MemberInit(Expression.New(type), binds);
var result = Expression.Lambda<Func<T>>(memberInit).Compile().Invoke();
return result;
}
我被难住的部分是如何在不引起装箱的情况下将值输入到最后两行
最佳答案
代替
var fieldParamExp = Expression.Parameter(fieldInfo.FieldType, "newVal");
尝试使用
var fieldParamExp = Expression.Constant(newVal);
更新:
为了高效的缓存你可以使用类似的东西
var startPar = Expression.Parameter(typeof (T), "start");
var endPar = Expression.Parameter(typeof (T), "end");
var amountPar = Expression.Parameter(typeof (float), "amount");
foreach (var fieldInfo in fields)
{
MethodInfo mi;
if (fieldInfo.FieldType.IsAssignableFrom(typeof (float)))
{
mi = typeof (Program).GetMethod("LerpF");
}
else
{
mi = typeof (Program).GetMethod("Lerp").MakeGenericMethod(fieldInfo.FieldType);
}
var makeMemberAccess = Expression.Call(mi, Expression.MakeMemberAccess(startPar, fieldInfo), Expression.MakeMemberAccess(endPar, fieldInfo), amountPar);
binds.Add(Expression.Bind(fieldInfo, makeMemberAccess));
}
var memberInit = Expression.MemberInit(Expression.New(type), binds);
var expression = Expression.Lambda<Func<T, T, float, T>>(memberInit, startPar, endPar, amountPar);
Func<T, T, float, T> resultFunc = expression.Compile();
// can cache resultFunc
var result = resultFunc(start, end, amount);
但我不知道你是如何决定使用开始或结束参数的,所以绑定(bind)中可能有一些更复杂的条件。
关于c# - 使用表达式树构造具有未知成员的对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20593830/