首页 > 解决方案 > C# 表达式树:对象数组到 Expression.New() 参数

问题描述

我想使用表达式树来提高一些对象关系映射代码的性能。旧代码如下所示:

public List<T> SqlToResults<T>(string query)
{
    // Do SQL stuff, get matching constructor for type T ...
    // ...

    List<T> results = new List<T>();

    // Create buffer for constructor parameters
    object[] constructorParams = new object[reader.FieldCount];
    
    while (reader.Read())
    {
        for (int i = 0; i < reader.FieldCount; i++)
        {
            // simplefied (there'd be some mapping to match correct order of parameters)
            constructorParams[i] = reader[i];
        }
        
        // create new instance of T with our data
        T result = (T)constructorInfoOfT.Invoke(constructorParams);
        
        // add new result to list of results
        results.Add(result);
    }
    return results;
}

上面代码中的性能瓶颈是ConstructorInfo.Invoke()我想用表达式树替换的调用和与此答案Expression.New()中的代码类似的调用。但是在编译时我不知道参数的数量及其类型,这似乎有点复杂。将一个表达式数组作为构造函数的参数,但我只有一个对象数组(这将是一个)。所以我必须以某种方式遍历 to 的内容,然后将每个元素映射到它自己的,然后可以作为to传递。Expression.New()ParameterExpressionParameterExpressionExpressionExpression[]Expression.New()

我想到的代码看起来像这样:

internal delegate TInstance Constructor<TInstance>(object[] parameters);

internal Constructor<T> BuildConstructorFrom<T>(ConstructorInfo constructorInfo)
{
    ParameterExpression constructorParameters = Expression.Parameter(typeof(object[]));

    Expression[] parameterExpressions;

    // ???
    // somehow map entries in constructorParameters to entries in parameterExpressions 
    // ???

    NewExpression constructorCall = Expression.New(constructorInfo, parameterExpressions);

    Constructor<T> ctor = (Constructor<T>)Expression.Lambda<Constructor<T>>(constructorCall, constructorParameters).Compile();
    return ctor;
}

我已经查看了类似的问题,例如使用表达式树的 foreach 循环构建动态表达式树时的问题,但我仍然不确定如何在我的用例中使用这些循环。

标签: c#reflectionormexpression-trees

解决方案


我设法弄清楚了。解决方案是使用Expression.ArrayIndex传统的 for 循环:

internal Constructor<T> BuildConstructerFrom<T>(ConstructorInfo constructorInfo)
{
    ParameterExpression constructorParameters = Expression.Parameter(typeof(object?[]));

    ParameterInfo[] parametersInfos = constructorInfo.GetParameters();

    Expression[] parameterExpressions = new Expression[parametersInfos.Length];
    for (int i = 0; i < parametersInfos.Length; i++)
    {
        ConstantExpression ithIndex = Expression.Constant(i);
        BinaryExpression ithParameter = Expression.ArrayIndex(constructorParameters, ithIndex);
        UnaryExpression unboxedIthParameter = Expression.Convert(ithParameter, parametersInfos[i].ParameterType);
        parameterExpressions[i] = unboxedIthParameter;
    }

    NewExpression constructorCall = Expression.New(constructorInfo, parameterExpressions);

    Constructor<T> ctor = (Constructor<T>)Expression.Lambda<Constructor<T>>(constructorCall, constructorParameters).Compile();
    return ctor;
}

推荐阅读