C#表达式添加两个对象的属性值

时间:2017-04-19 18:22:53

标签: c# lambda expression

我需要一种有效的方法来对两个相同类型的对象中的任意属性求和。

我有一个具有大量不同数值类型属性的类:

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);

根据我的研究,它将涉及BlockExpressionExpression.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

2 个答案:

答案 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的总和

编辑:格式化很糟糕