Можете ли вы перегрузить Sum для добавления пользовательских типов
У меня есть структура 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 };
}
}
Учитывая приведенный выше код, если у меня есть список элементов, имеющих объекты денежных значений, можно получить функцию Sum для работы с объектом Money value.
т
Items.Sum(m => m.MoneyValue);
Ответы
Ответ 1
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);
Ответ 2
Операторы - это боль. Однако, если вы посмотрите 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;
}
Ответ 3
Это должно работать, если вы вернете m.MoneyValue.Amount
вместо этого - набор перегрузок Sum
, которые принимают Func
.
К сожалению, Sum
не подчиняется никакому operator+
, который вы определяете. (Фактически, он не мог вызвать operator+
без использования отражения.)