你可以重载Sum来添加自定义类型

时间:2010-11-09 10:49:52

标签: c# linq ienumerable

我有一个货币和金额的Money结构。我希望能够使用linq来汇总一个列表。

public struct Money
{
    public string Currency { get; set; }
    public decimal Amount { get; set; }

    public static Money operator +(Money m1, Money m2)
    {
        if (m1.Currency != m2.Currency)
            throw new InvalidOperationException();

        return new Money() { Amount = m1.Amount + m2.Amount, Currency = m1.Currency };
    }
}

鉴于上面的代码,如果我有一个具有Money值对象的Items列表,则可以使Sum函数与Money值对象一起使用。

Items.Sum(m => m.MoneyValue);

4 个答案:

答案 0 :(得分:26)

public static class SumExtensions
{
    public static Money Sum(this IEnumerable<Money> source)
    {
        return source.Aggregate((x, y) => x + y);
    }

    public static Money Sum<T>(this IEnumerable<T> source, Func<T, Money> selector)
    {
        return source.Select(selector).Aggregate((x, y) => x + y);
    }
}

用法:

IEnumerable<Money> moneys = ...
Money sum = moneys.Sum();

IEnumerable<Transaction> txs = ...
Money sum = txs.Sum(x=>x.Amount);

答案 1 :(得分:3)

操作员很痛苦。但是,如果您查看MiscUtil,我已经实现了 尊重自定义运算符的通用Enumerable.Sum。用法是(有意)相同 - 所以你的行:

var moneySum = Items.Sum(m => m.MoneyValue);

应该可以使用预期的结果 - 除了您当前没有处理default(Money)用于添加目的。或者,如果MoneyValue ,则只需编写扩展方法:

public static class MoneyExtensions {
    public static Money Sum(this IEnumerable<Money> source) {
        Money sum = source.First();
        foreach(var item in source.Skip(1)) sum += item;
        return sum;
    }
}

实际上,为了避免2次枚举,我可能会将其调整为:

using (var iter = source.GetEnumerator())
{
    if (!iter.MoveNext()) return default(Money);
    var sum = iter.Current;
    while (iter.MoveNext()) sum += iter.Current;
    return sum;
}

答案 2 :(得分:0)

如果您返回m.MoneyValue.Amount而不是a set of Sum overloads that accept a Func

,这应该有效

不幸的是Sum不遵守您定义的任何operator+。 (事实上​​,如果不使用反射,它就无法调用operator+。)

答案 3 :(得分:0)

我知道这很老了。但是我在系统中有一个类似的Money类。 我已将Sum实现更改为Jesper的执行方式。

我没有default的钱,但是我为解决空集合的情况所做的就是添加汇总的种子。

public static class SumExtensions
{        
    public static Money Sum(this IEnumerable<Money> source)
        => source.Aggregate(new Money(0), (x, y) => x + y);

    public static Money Sum<T>(this IEnumerable<T> source, Func<T, Money> selector)
        => source.Select(selector).Sum();
}

我的实现处理添加没有货币的Monies。其他任何情况都将引发DifferentCurrenciesException。