如何创建可以处理或强制转换未知类型的 lambda 表达式

本文关键字:转换 未知 类型 表达式 lambda 处理 何创建 创建 | 更新日期: 2023-09-27 18:33:04

如何为可以处理未知类型的函数创建lambda表达式?对不起,我知道这个问题很模糊,我很难形成它。我只能希望你能花一分钟时间通读我的故事,这应该会澄清一些问题。

我的目标是使用预定义的数据协定将字符串值数组反序列化为对象。数据协定的成员有一个职位编号。反序列化程序的简单工作是将值映射到数据成员(在执行适当的类型转换之后(,并生成对象。

问题是反序列化性能很糟糕!运行VS Profiler后,我发现用于填充对象成员的PropertyInfo.SetValue((花费的时间最多。我的程序必须在任何给定时间反序列化数千个对象。数据协定通常有 100 个成员。所以我们说每 1000 个对象对 SetValue(( 的 100,000 次调用,它正在拖动。下面是对 SetValue 的调用示例:

// for each data contract type
// go through each property and set the value
foreach(PropertyInfo pi in pis)
{
    object data = convertStringToMemberType(pi, attributeArray, valueStringArray);
    pi.SetValue(objectToBuild, data, null);
}

然后我从未知食谱中找到了这个页面,它有一个有希望的解决方案来解决这个性能问题。看起来我需要使用编译的 lambda 表达式来替换 SetValue,但我遇到了转换问题。按照上面链接中的示例,我现在有 SetValue(( 的替代品。替换是操作委托,它们是已编译的 lambda 表达式。

首先,我扩展了 PropertyInfo 类。

public static class PropertyInfoExtensions
{
    public static Action<object, object> GetValueSetter(this PropertyInfo propertyInfo)
    {
        var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
        var argument = Expression.Parameter(typeof(object), "a");
        var setterCall = Expression.Call(
            instance,
            propertyInfo.GetSetMethod(),
            Expression.Convert(argument, propertyInfo.PropertyType));
        return (Action<object, object>)Expression.Lambda(setterCall, instance, argument).Compile();
    }
}

然后,我构建了一个 Dictionary<PropertyInfo, Action<object, object> 对象,它将每个 propertyInfo 对象绑定到其相应的 Action 委托。这样我就可以"缓存"编译的 lambda 并在一批反序列化中重用它。这就是我现在所说的:

foreach(PropertyInfo pi in pis)
{
    object data = convertStringToMemberType(pi, attributeArray, valueStringArray);
    var setValueDelegate = _actionDelegateDict[pi];
    setValueDelegate(objectToBuild, data);
}

但是,我收到以下异常:

Unable to cast object of type 'System.Action`2[Test.DataContract1,System.Object]' to type 'System.Action`2[System.Object,System.Object]'.

这里的 DataContract1 是我尝试构建的对象类型。它仅在运行时已知,这与未知配方示例中的方案不同,其中类型在编译时是已知的。您将如何使这个 lambda 表达式工作?

非常感谢您的时间!

如何创建可以处理或强制转换未知类型的 lambda 表达式

听起来很像我对 FastReflection 库所做的。你几乎就在那里,你只需将实例参数更改为对象类型,然后将该表达式强制转换为实际类型。

我认为您现在拥有的代码如果更改为此代码将起作用。

public static class PropertyInfoExtensions
{
    public static Action<object, object> GetValueSetter(this PropertyInfo propertyInfo)
    {
        var instance = Expression.Parameter(typeof(object), "i");
        var argument = Expression.Parameter(typeof(object), "a");
        var setterCall = Expression.Call(
            Expression.Convert(instance, propertyInfo.DeclaringType),
            propertyInfo.GetSetMethod(),
            Expression.Convert(argument, propertyInfo.PropertyType));
        return Expression.Lambda<Action<object,object>>(setterCall, instance, argument).Compile();
    }
}

想象一下,您直接创建此lambda,而不是使用Expression s。那会是什么样子?您要创建Action<object, object>

Action<object, object> action = (object i, object a) => i.Property = a;

但这行不通,您需要同时投ia.所以:

Action<object, object> action =
    (object i, object a) => ((DataContract)i).Property = (PropertyType)a;

在你的代码中,你正在转换a,但你也需要转换i

public static Action<object, object> GetValueSetter(this PropertyInfo propertyInfo)
{
    var instance = Expression.Parameter(typeof(object), "i");
    var argument = Expression.Parameter(typeof(object), "a");
    var setterCall = Expression.Call(
        Expression.Convert(instance, propertyInfo.DeclaringType),
        propertyInfo.GetSetMethod(),
        Expression.Convert(argument, propertyInfo.PropertyType));
    return (Action<object, object>)Expression.Lambda(setterCall, instance, argument).Compile();
}