2010-11-09 21 views
11

Tengo una estructura de dinero que tiene moneda y cantidad. Me gustaría poder sumar una lista usando linq.Puede sobrecargar Suma para agregar tipos personalizados

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

Teniendo en cuenta el código anterior si tengo una lista de artículos que tienen objetos de valor del dinero es posible conseguir la función Suma de trabajar con un objeto de valor del dinero.

es decir

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

En realidad, parece que su + no estará 100% contento con la suma genérica MiscUtil, debido al uso de 'default (T)'; Me pregunto si debería cambiar 'Money' para que sea más generoso, es decir,' someValue + default (Money) => someValue' –

+2

También - struct mutable; * awoooga *, * awoooga * ... –

Respuesta

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

Uso:

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

y

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

+1. Bonito y limpio. – Steven

+0

¿puede por favor también proporcionar el uso de estos métodos? –

+0

probablemente quieras hacer que esa clase sea pública. Me tomó un minuto darme cuenta de por qué el mío no estaba compilando (definido en diferentes DLL) –

0

Esto debería funcionar si regresa m.MoneyValue.Amount lugar - hay a set of Sum overloads that accept a Func.

Desafortunadamente Sum no obedece ningún operator+ que usted defina. (De hecho, no pudo llamar al operator+ sin usar el reflejo.)

+0

El OP debe tener en cuenta que esto no tomará en cuenta las diferentes monedas de la lista. – TeaDrivenDev

3

Los operadores son un dolor. Sin embargo, si nos fijamos en MiscUtil, he implementado un Enumerable.Sum genérico que hace respecto a los operadores personalizados. El uso es (intencionalmente) idéntico - por lo que su línea:

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

debería funcionar, con el resultado esperado - excepto que actualmente no maneja default(Money) con fines de suma. Alternativamente, si esto es simplemente para MoneyValue, acaba de escribir un método de extensión:

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

En realidad, para evitar 2 enumeraciones, podría modificar esto:

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