我需要一个有效的方法来总结相同类型的两个对象中的任意属性。C#表达式来添加两个对象的属性值
我有一个类具有大量不同数值类型的属性:
public class MyClass
{
public int Field1 { get; set; }
public long Field2 { get; set; }
public float Field3 { get; set; }
public double Field4 { get; set; }
//...
public uint Field100 { get; set; }
}
在运行时,我允许用户选择这些字段的任意子集:
List<PropertyInfo> props = new List<PropertyInfo>();//Field1, Field5, Field99 etc...
我然后需要迭代两个对象上的所有选定属性,将它们相加,并将其重新分配给第一个对象:
MyClass mc1 = new MyClass();
MyClass mc2 = new MyClass();
SumProps(mc1, mc2, props);
这对于使用字段1,5和99上面的例子中,将具有这样的效果:我目前使用的反射与PropertyInfo.GetValue()/SetValue()
并手动浇铸到合适的类型
mc1.Field1 += mc2.Field1;
mc1.Field5 += mc2.Field5;
mc1.Field99 += mc2.Field99;
。这太慢了,因为这是将被称为数十亿次的代码的性能关键部分。
因此,我需要一种生成表达式Lambda的方法,该方法将使用适当的类型生成汇总所有请求字段的代码。然后,我将调用拉姆达,如:
MySumPropsLambda(c1, c2);
从我研究过它会涉及BlockExpression
和Expression.AddAssign
,但我不能完全换我围绕如何真正完成它的头。
我使用Visual Studio 2013和.NET 4.5.1
编辑:得益于以下阿卡什卡瓦,我做了轻微的修改,现在用这个解决方案:
public static Action<T, T> MakePropertySummationAction<T>(PropertyInfo[] props)
{
var mc1 = Expression.Parameter(typeof(T));
var mc2 = Expression.Parameter(typeof(T));
var exps = new List<Expression>();
foreach (var pi in props)
{
var p1 = Expression.Property(mc1, pi.Name);
var p2 = Expression.Property(mc2, pi.Name);
exps.Add(Expression.AddAssign(p1, p2));
}
var blockExpr = Expression.Block(exps);
return Expression.Lambda<Action<T, T>>(blockExpr, mc1, mc2).Compile();
}
打印块表达式字符串证明它生成了所需的代码:
(Param_0.Field1 += Param_1.Field1)
(Param_0.Field2 += Param_1.Field2)
(Param_0.Field3 += Param_1.Field3)
(Param_0.Field4 += Param_1.Field4)
执行一百万次求和的性能毫秒:
Direct took 4.8ms
Compiled lambda took 177.5ms
Reflection took 5376.7ms
我不知道如果我理解这个问题完全因此评论,而不是一个答案,但出于好奇,为什么你不能用正确的数据结构如字典做到这一点? –
代码味道警报!具有大量数值属性的类不是一个好主意。你可以使用数组的值吗? – ja72
你可以尝试使用像T4这样的代码生成器来生成你想要的'Add'方法。您必须使用反射来告诉生成器如何在构建时扫描类型的属性,但是在运行时不会有任何开销。 – JamesFaix