c# - 使用表达式树构造具有未知成员的对象

标签 c# c#-4.0 dynamic expression-trees

我正在尝试创建一个通用函数,它将采用一个结构的 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/

相关文章:

c# - 将 3 轴磁力计读数和加速度计转换为罗盘方位 (Win 7)

c# - 当尝试从 WCF 服务支持双 LAN 时,WCF 内部异常消息 "A registration already exists for URI ' net.tcp ://. .. .' "

visual-studio - 如何将 Visual Studio 2013 更新 4 升级到更新 5?

c# - native 动态 Linq (C#)

dynamic - prolog 动态谓词/1 和/2 之间的区别

c# - 在 C# 中将字符串分成两个或多个部分

c# - 在 Crystal 报表中水平重复对象?

c# - 限制密码输错3次的用户

c# - 如何在泛型的 where 子句中指定泛型类?

php - 使用 jQuery 编辑多个输入