Reputation: 18485
First I followed this tutorial to create my Money object: https://www.codeproject.com/articles/837791/money-pattern
Money totalItems = _invoice.InvoiceDetails
.Sum(y => y.Amount); // Amount is of type Money
I get an compilation exception on y.Amount
:
Cannot implicitly convert type 'Money' to 'long?' Cannot convert lambda expression to intended delegate type because some of the return types in the block are not implicitly convertible to the delegate return type
What am I doing wrong?
Here is my Money class:
public class Money
{
public decimal Amount { get; private set; }
public CurrencyCode Currency { get; private set; }
#region Constructors
public Money() { }
public Money(Money amount)
{
this.Amount = amount.Amount;
this.Currency = amount.Currency;
}
public Money(decimal amount, CurrencyCode currencyCode)
{
this.Amount = amount;
this.Currency = currencyCode;
}
public Money(int amount, CurrencyCode currency)
: this(Convert.ToDecimal(amount), currency)
{
}
public Money(double amount, CurrencyCode currency)
: this(Convert.ToDecimal(amount), currency)
{
}
#endregion
#region Comprasion operators
public static bool operator ==(Money var1, Money var2)
{
if ((object)var1 == null || (object)var2 == null)
return false;
if (var1.Currency != var2.Currency) return false;
return var1.Amount == var2.Amount;
}
public static bool operator !=(Money var1, Money var2)
{
return !(var1 == var2);
}
public static bool operator >(Money var1, Money var2)
{
if (var1.Currency != var2.Currency)
throw new InvalidOperationException("Comprasion between different currencies is not allowed.");
return var1.Amount > var2.Amount;
}
public static bool operator <(Money var1, Money var2)
{
if (var1 == var2) return false;
return !(var1 > var2);
}
public static bool operator <=(Money var1, Money var2)
{
if (var1 < var2 || var1 == var2) return true;
return false;
}
public static bool operator >=(Money var1, Money var2)
{
if (var1 > var2 || var1 == var2) return true;
return false;
}
#endregion
#region Ariphmetical operations
public static Money operator +(Money var1, Money var2)
{
if (var1.Currency != var2.Currency)
{
throw new InvalidCastException("Calculation is using different currencies!");
}
return new Money(var1.Amount + var2.Amount, var1.Currency);
}
public static Money operator -(Money var1, Money var2)
{
if (var1.Currency != var2.Currency)
{
throw new InvalidCastException("Calculation is using different currencies!");
}
return new Money(var1.Amount - var2.Amount, var1.Currency);
}
public static Money operator *(Money var1, Money var2)
{
if (var1.Currency != var2.Currency)
{
throw new InvalidCastException("Calculation is using different currencies!");
}
return new Money(var1.Amount * var2.Amount, var1.Currency);
}
public static Money operator /(Money var1, Money var2)
{
if (var1.Currency != var2.Currency)
{
throw new InvalidCastException("Calculation is using different currencies!");
}
return new Money(var1.Amount / var2.Amount, var1.Currency);
}
public static Money operator *(decimal var1, Money var2)
{
return new Money(var1 * var2.Amount, var2.Currency);
}
public static Money operator *(Money var1, decimal var2)
{
return new Money(var1.Amount * var2, var1.Currency);
}
public static Money operator /(decimal var1, Money var2)
{
return new Money(var1 / var2.Amount, var2.Currency);
}
public static Money operator /(Money var1, decimal var2)
{
return new Money(var1.Amount / var2, var1.Currency);
}
public static Money operator *(int var1, Money var2)
{
return new Money(var1 * var2.Amount, var2.Currency);
}
public static Money operator *(Money var1, int var2)
{
return new Money(var1.Amount * var2, var1.Currency);
}
public static Money operator /(int var1, Money var2)
{
return new Money(var1 / var2.Amount, var2.Currency);
}
public static Money operator /(Money var1, int var2)
{
return new Money(var1.Amount / var2, var1.Currency);
}
public static Money operator *(long var1, Money var2)
{
return new Money(var1 * var2.Amount, var2.Currency);
}
public static Money operator *(Money var1, long var2)
{
return new Money(var1.Amount * var2, var1.Currency);
}
public static Money operator /(long var1, Money var2)
{
return new Money(var1 / var2.Amount, var2.Currency);
}
public static Money operator /(Money var1, long var2)
{
return new Money(var1.Amount / var2, var1.Currency);
}
#endregion
public override bool Equals(object obj)
{
if (obj == null) return false;
Money money = obj as Money;
return (this.Amount == money.Amount && this.Currency == money.Currency);
}
public bool Equals(Money money)
{
if ((object)money == null) return false;
return (this.Amount == money.Amount && this.Currency == money.Currency);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public override string ToString()
{
return this.Amount.ToString();
}
#endregion
Upvotes: 5
Views: 3608
Reputation: 71
I recently solved a similar problem with the following two extension methods.
public static Money Sum<T>(this IEnumerable<T> source, Func<T, Money> selector)
=> Sum(source.Select(selector));
public static Money Sum(this IEnumerable<Money> money)
=> money.Aggregate((left, right) => left + right);
Upvotes: 1
Reputation: 116585
Presumably InvoiceDetails
is a collection of classes that contain a public Money Amount
property, e.g.:
public class InvoiceDetail
{
public Money Amount { get; set; }
}
In that case, you can use Enumerable.Aggregate()
to do the sum:
var sum = InvoiceDetails.Aggregate(new Money(0, InvoiceDetails.First().Amount.Currency), (s, d) => s + d.Amount);
To get rid of the slightly ugly new Money(0, InvoiceDetails.First().Amount.Currency)
expression you might want to introduce a special singleton Money.Empty
that contains no money and can be added to any type of money. Or modify the static operators to accept a null
value for Money
and do:
var sum = InvoiceDetails.Aggregate((Money)null, (s, d) => s + d.Amount);
Alternatively, introducing an intermediate Select()
might make the expression cleaner:
var sum = InvoiceDetails.Select(d => d.Amount).Aggregate((s, a) => s + a);
The reason that Enumerable.Sum()
does not work is that it is defined for a fixed set of enumerable arithmetic types. There is no Sum()
for arbitrary types for which arithmetic operator overloads have been introduced, as there's no common interface or type inferencing for such a scenario. (See Is there a constraint that restricts my generic method to numeric types?, to which the answer is, "no".) Of course you could add your own version of Enumerable.Sum()
that supports types that provide their own arithmetic, see e.g. this answer for a place to start.
Upvotes: 6
Reputation: 15772
In a similar vein to @dbc's answer. You need to create your own concrete example of the Sum
method. This is because operator
is static, and not interface-able.
I would actually go a slightly different route from dbc because
Enumerable.First()
causes multiple iterations of the IEnumerable
Enumerable.First()
will fail when there are no elements, but will give a poor exception.Instead I prefer.
namespace System.Linq //Using System.Linq namespace makes it easier to use
{
public static class MoneyEnumerable
{
public static Money Sum(this IEnumerable<Money> monies)
{
return monies.Aggregate((left, right) => left + right);
}
}
}
Upvotes: 3
Reputation: 247018
You could add an implicit operator that would allow the object to be converted to a type that can be summed.
public static implicit operator decimal(Money money) {
return money != null ? money.Amount : 0;
}
Upvotes: 0