Is there a way to avoid needing to round all my values in the UI they are displayed it, could I do it better via some formatter class perhaps?
Yes:
public string ToString(string format, IFormatProvider provider)
{
return Currency + " " + RawAmount.ToString(format, provider);
}
public string ToString(string format)
{
return ToString(format, NumberFormatInfo.CurrentInfo);
}
public string ToString(IFormatProvider provider)
{
return ToString("0.00", provider);
}
public override string ToString()
{
return ToString(provider);
}
You can go much beyond this, but now just how such an object is presented is up to the code presenting it (but with a two-decimal place default).
If I'm working with complex calculations, what's the best way to generate test values. E.g. If I use a decimal for my Amount property and want to test the output of 10/3, how would you recommend I check the result without just re-phrasing the calculation?
Rephrasing the calculation outside of the class. Assert.Equal(10/3, (new Amount(10) / 3).Value);
Is rounding each composite Amount is the way to go? and just accept the inaccuracy? does anyone have experience of apps dealing with financials (note this is not a serious banking app or anything so perhaps fractions of pence may not be too much of a problem.)
This depends on the reason it's being used. If you are tallying a bunch of things presented to a user at a given price, then you are going to have to add the rounded values up, because you said you were charging them USD 12.03 and USD 3.12 and so they expect a total of USD 15.15 as that's the sum of what they were told about, not USD 15.16 because that's the sum of 12.034 and 3.1246 when rounded from USD 15.1586.
If you are tallying a bunch of small items including perhaps currency exchange etc. you are going to have to keep all of those small fractions until nearer the end.
You are also going to have to have a decision about whether to apply Bankers Rounding (the default, and the one to go for most of the time) or school-child rounding (the sort expected by some customers, should the details prior to the rounding be visible to them), or always round down (has been legally insisted upon during some currency-change transition periods).
Really, for any given financial process, which sort of rounding, to what degree, and when it is applied is one of the things that need to be recorded in the rules for that process. It is not something that you can generalise about in a general-purpose class.
If you were going to have such a class, then it would make more sense to have this as an operation on the objects themselves:
public Money Round(int decimals, MidpointRounding mode)
{
return new Money(Currency, Amount.Round(decimals, mode));
}
public Money Round(MidpointRounding mode)
{
return Round(2, mode);
}
public Money Round(int decimals)
{
return Round(decimals, MidpointRounding.ToEven);
}
public Money Round()
{
return Round(2);
}
There; the user code can decide when to round, and also how to round but with sensible defaults.
I'd also recommend making such a class a struct
and definitely to make it immutable.