A convenient money structure for the CLR which carefully handles rounding and currency types.
The CLR doesn't include a native money type. This could be seen as a shortcoming, or a reasonable design decision, given that it is an object-oriented framework, and adding your own type to encapsulate the data and behavior you need is pretty much its reason
Money, however, is a type which is so primitive, and so pervasive, that it's absence is notable. Other primitive types are built in, after all. Martin Fowler considers this in
Patterns of Enterprise Application Architecture
A large proportion of the computers in this world manipulate money, so it's always puzzled me that money isn't actually a first class data type in any mainstream programming language. The lack of a type causes problems, the most obvious surrounding
The good thing about object-oriented programming is that you can fix these problems by creating a Money class that handles them. Of course, it's still surprising that none of the mainstream base class libraries actually do this.
], Martin Fowler and Matt Foemmel's example shows Money as an object with value semantics. In .Net, a value type is a first-class entity, and so it makes sense to combine these two and make Money a value type. This should also help deal with
the issue of performance the authors bring up, since value types are not reference counted on the heap, meaning less pressure on the GC and therefore higher performance.
Also in [PEAA
], the underlying type used to store the value was an Int64 (long). The authors then scale the value by some power of 10 (0 - 3) to represent fractional units. In this type, I opted to represent the fraction as a completely separate Int32,
which is scaled by 10^9 (the largest power of 10 which fits into an Int32). This allows storing much smaller fractions, which is useful for intermediate computations. Due to this, the type a fixed-decimal point numeric representation. This is how relational
databases often store money, so it makes a natural fit. Another alternative was to represent the money value with a System.Decimal, as is done here:
. The problem with this approach is that System.Decimal is a binary floating-point type, and binary floating-point types give us
all sorts of headaches when computing with them and not treating the round-off with extreme care. These issues can be avoided by working with whole numbers and scaling to represent fractions.
I opted to separate the responsibility to allocate money in a defined distribution into another class: MoneyDistributor. The reasons for this are that I think it helps readability and conscientious use, and having both a divide operation and an allocate operation
on the money class seemed to be a bit of a conflict of interest. By separating out the responsibility for not losing (or gaining!) fractions of money into a separate class, I force Money to admit that it can't really do a good job at dividing itself up,
and rather entrusts this to another class. Further, this then allows subclassing of MoneyDistributor for custom behavior, which can no longer be done with Money, since it is a value type.
With regard to the Currency aspect of the type, .Net doesn't have this, but Java does. Java follows ISO 4217, and so it is pretty safe to add something like this to .Net - just follow the spec. My first inclination was to just make a Currency class with
static fields: one for each currency. However,
implementation used a CultureInfo instance to represent the currency, and it got me to think about how this is already somewhat present in the BCL. Since the CultureInfo classes are built around an Int32 identifier known as the LCID, it
seems reasonable to use this as the currency identifier as well. However, after some observation, this appears not to be a sound approach: not only are the culture-to-currency relationships not 1-to-1, but they are also not N-to-1, since some cultures use
more than one currency. In the end, I used the ISO spec to generate some lookup tables, and keep everything related based on the ISO numeric code for any given currency. This code is an Int32, and this serves as the only field in a Currency instance, allowing
me to represent the Currency as a value type as well, making serialization of Money quite simple: there are no reference fields in it! One last functionality of currency to point out: IFormatProvider is implemented on it, so that when ToString() is called
on Money, the associated Currency instance is passed with it and it gets formatted as expected.