【发布时间】:2021-06-14 10:50:44
【问题描述】:
我想使用表达式树来提高一些对象关系映射代码的性能。旧代码如下所示:
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() 的调用替换它,类似于this answer 中的代码。但是在编译时我不知道参数的数量及其类型,这似乎有点复杂。 Expression.New() 将 Expressions 数组作为构造函数的参数,但我只有一个对象数组(这将是单个 ParameterExpression)。所以我必须以某种方式遍历ParameterExpression 的内容,然后将每个元素映射到它自己的Expression,然后可以将其作为Expression[] 传递给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 loop using expression trees 和 issue while building dynamic expression tree,但我仍然不确定如何在我的用例中使用这些循环。
【问题讨论】:
-
您应该使用
ConstructorInfo.GetParameters来获取构造函数期望的参数列表,然后在此基础上创建ParameterExpression的数组(或列表),同时注意检查每个参数的类型参数并从对象执行适当的转换。但是,您仍然会有一个委托接受要传递给新表达式的对象数组。它可以工作,但它的性能仍然受到其通用方法的影响,其中一个对象数组包含参数的值。也可能涉及价值拳击。
标签: c# reflection orm expression-trees