我有一个货币和金额的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);
答案 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。