我需要一种有效的方法来对两个相同类型的对象中的任意属性求和。
我有一个具有大量不同数值类型属性的类:
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的示例,将具有以下效果:
mc1.Field1 += mc2.Field1;
mc1.Field5 += mc2.Field5;
mc1.Field99 += mc2.Field99;
我目前正在使用PropertyInfo.GetValue()/SetValue()
的反射并手动转换为适当的类型。这太慢了,因为这是代码的性能关键部分,将被称为数十亿次。
所以我需要一种生成Expression Lambda的方法,它将使用适当的类型生成用于求和所有请求字段的代码。然后我会将那个lambda称为:
MySumPropsLambda(c1, c2);
根据我的研究,它将涉及BlockExpression
和Expression.AddAssign
,但我无法完全理解如何实现它。
我正在使用Visual Studio 2013和.NET 4.5.1
编辑:感谢下面的Akash Kava,我做了一些修改并使用了这个解决方案:
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
答案 0 :(得分:2)
工作小提琴
https://dotnetfiddle.net/xPrXXG
mc1.Field1 += mc2.Field2;
Lambda Expression的等价物是....
Action<MyClass,MyClass> CreateMethod(string propertyName){
var mc1 = Expression.Parameter(typeof(MyClass));
var mc2 = Expression.Parameter(typeof(MyClass));
var p1 = Expression.Property(mc1, propertyName);
var p2 = Expression.Property(mc2, propertyName);
var assign = Expression.AddAssign(p1,p2);
return Expression.Lambda<Action<MyClass,MyClass>>
(assign,mc1,mc2).Compile();
}
你可以打电话给......
var addAssign1 = CreateMethod("Field1");
// equivalent to mc1.Field1 += mc2.Field1;
addAssign1(mc1,mc2);
答案 1 :(得分:0)
您正在寻找的是运营商重载:
例如,假设您有T a和T b, 你可以这样做:
public class T{
public int Field1{get;set;}
public static T operator +(T c1, T c2)
{
return new T{Field1=c1.Field1+c2.Field1;}
}
}
然后你可以这样做:
T a,b; //initilaize here
T c= a+b;
获取新编译值作为a和b的总和
编辑:格式化很糟糕