LINQ求和所有属性

本文关键字:属性 求和 LINQ | 更新日期: 2023-09-27 18:30:02

有一种优雅的方法可以做到这一点吗?

假设我处理的对象具有排除性的数字属性(int和doubles),比如:

Foo
    -bar1 int
    -bar2 int
    -bar3 int
    -foobar1 double
    -foobar2 double

我有一个列表集。。。有没有一种方法可以让它简单地对List中的所有数字属性求和,并返回一个包含所有总数的对象Foo?

一如既往地感谢SO

LINQ求和所有属性

如果我正确理解了你的问题,并且你想得到一个Foo实例,其中每个成员是列表中存在的Foo实例的所有相应成员的总和,那么一种可能的方法(有点冗长,但简单,不涉及魔术)是使用linqAggregate:

// Simplified Foo to prevent a lot of typing.
public class Foo
{
    public int bar1 { get; set; }
    public int bar2 { get; set; }
    public double foobar1 { get; set; }
}
var myFooList = new List<Foo>
{
    new Foo() { bar1 = 1, bar2 = 2, foobar1 = 20.0 },
    new Foo() { bar1 = 5, bar2 = 8, foobar1 = 42.0 },
    new Foo() { bar1 = 9, bar2 = 3, foobar1 = -10.0 },
};
var myFooSum = myFooList.Aggregate(new Foo(), (curSum, foo) =>
{
    curSum.bar1 += foo.bar1;
    curSum.bar2 += foo.bar2;
    curSum.foobar1 += foo.foobar1;
    return curSum;
});
Console.WriteLine("FooSum: bar1 = {0}, bar2 = {1}, bar3 = {2}", myFooSum.bar1, myFooSum.bar2, myFooSum.foobar1);

打印:

FooSum: bar1 = 15, bar2 = 13, bar3 = 52

如果我误解了,你想把一个任意的对象列表(其中一些可能包含多个数字字段)求和为一个单一的和值,@Simon Whitehead建议的方法可以奏效:

public class Bar
{
    public int baz { get; set; }
    public double booz { get; set; }
    public string nonNumeric { get; set; }
}
static double SumNumericalProperties(object obj)
{
    if (obj == null)
        return 0;
    if (obj is int)
        return (int)obj;
    if (obj is double)
        return (double)obj;
    // etc for other numeric types.
    var sum = 0.0;
    foreach (var prop in obj.GetType().GetProperties())
    {
        if (typeof(int).IsAssignableFrom(prop.PropertyType))
            sum += (int)prop.GetValue(obj);
        else if (typeof(double).IsAssignableFrom(prop.PropertyType))
            sum += (double)prop.GetValue(obj);
        // etc for other numeric types.
    }
    return sum;
}
var myObjectList = new List<object>
{
    new Foo() { bar1 = 1, bar2 = 2, foobar1 = 20.0 },
    new Bar() { baz = 10, booz = 100.0 },
    24,
    33.3333,
    new Foo() { bar1 = 5, bar2 = 8, foobar1 = 42.0 },
    new Foo() { bar1 = 9, bar2 = 3, foobar1 = -10.0 },
};
var myFooSum = myObjectList.Sum(SumNumericalProperties);
Console.WriteLine("Sum = {0}", myFooSum);

打印:

Sum = 247.3333

也可以使用以下一行来完成:-)

Item sum = (from p in typeof(Item).GetFields()
          where Type.GetTypeCode(p.FieldType) == TypeCode.Int32 || Type.GetTypeCode(p.FieldType) == TypeCode.Double
          group p by p into g
          select new { Prop=g.Key, Sum=items.Sum(s => (decimal)Convert.ChangeType(g.Key.GetValue(s), TypeCode.Decimal)) })
          .Aggregate(new Item(), (state, next) => {  
            next.Prop.SetValue(state, Convert.ChangeType(next.Sum, next.Prop.FieldType));
            return state;
          });

完整样品:

using System;
using System.Linq;
using System.Collections.Generic;
class Program
{
    class Item
    {
        public int bar1 = 0;
        public int bar2 = 0;
        public double foobar1 = 0;
        public double foobar2 = 0;
    }
    public static void Main ()
    {
        var items = new List<Item>();
        items.Add(new Item() { bar1 = 1, bar2 = 2, foobar1 = 1.1, foobar2 = 1.2 });
        items.Add(new Item() { bar1 = 1, bar2 = 2, foobar1 = 1.1, foobar2 = 1.2 });
        items.Add(new Item() { bar1 = 1, bar2 = 2, foobar1 = 1.1, foobar2 = 1.2 });
        var sum = (from p in typeof(Item).GetFields()
                  where Type.GetTypeCode(p.FieldType) == TypeCode.Int32 || Type.GetTypeCode(p.FieldType) == TypeCode.Double
                  group p by p into g
                  select new { Prop=g.Key, Sum=items.Sum(s => (decimal)Convert.ChangeType(g.Key.GetValue(s), TypeCode.Decimal)) })
                  .Aggregate(new Item(), (state, next) => {  
                    next.Prop.SetValue(state, Convert.ChangeType(next.Sum, next.Prop.FieldType));
                    return state;
                  });

        Console.WriteLine("bar1: " + sum.bar1);
        Console.WriteLine("bar2: " + sum.bar2);
        Console.WriteLine("foobar1: " + sum.foobar1);
        Console.WriteLine("foobar2: " + sum.foobar2);
    }
}

粗略尝试:

    private static double sumNumericalProperties<T>(T obj)
    {
        var result = 0d;
        foreach (var prop in typeof (T).GetProperties())
        {
            if (typeof(int).IsAssignableFrom(prop.PropertyType))
            {
                result += (int)prop.GetValue(obj);
            }
            else if (typeof(double).IsAssignableFrom(prop.PropertyType))
            {
                result += (double) prop.GetValue(obj);
            }
        }
        return result;
    }

示例输入:

class Foo
{
    public int Bar1 { get; set; }
    public int Bar2 { get; set; }
    public double Foobar1 { get; set; }
    public double Foobar2 { get; set; }
    public string BufferProperty { get; set; }
}
var obj = new Foo() {Bar1 = 2, Bar2 = 4, Foobar1 = 5, Foobar2 = 6};
var obj2 = new Foo() { Bar1 = 2, Bar2 = 4, Foobar1 = 5, Foobar2 = 7 };
var list = new List<Foo>();
list.Add(obj);  // 17
list.Add(obj2); // 18

Console.WriteLine(list.Sum(x => sumNumericalProperties(x))); // 35