LINQ总和所有属性

时间:2013-09-04 01:08:15

标签: c# .net linq

有优雅的方法吗?

假设我正在处理具有令人难以置信的数字属性(整数和双精度)的对象,如下所示:

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

我有一个List的集合...有没有办法让它简单地对List中的所有数字属性求和并返回一个具有所有总数的单个对象Foo?

一如既往地谢谢你

3 个答案:

答案 0 :(得分:2)

如果我正确理解了您的问题,并且您希望获得一个Foo实例,其中每个成员是列表中存在的Foo个实例的所有相应成员的总和,那么一种可能的(有点冗长,但直截了当,没有涉及魔法)的方法是使用linq Aggregate:

// 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

答案 1 :(得分:2)

也可以使用这种单线程来完成: - )

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

答案 2 :(得分:1)

粗暴尝试:

    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