Andre de Cavaignac

Let's blog it out...

Counting Money and Handling Currency in .NET (Money Value Types, Sample Code)

Sample VS.NET 2005 Solution For Download Here

One large missing piece I have found in the .NET Framework recently was any support for calculating money.  Money is not particularly difficult to calculate, seeing that it is merely a decimal value, however it is difficult to do properly, without making serious mistakes.  The use of multiple currencies in the same application could potentially lead to serious calculation errors (such as adding Euro and US Dollar together).  To avoid this, and make passing money around in an application easier, I created a Money struct that I thought I would share.

At the onset, this task seemed much easier than it actually was.  A simple struct to hold both currency and a decimal value.  Unfortunately, more complexities soon found their way into the mix, including formatting string, holding and validating currency codes, and (worse of all) comparison.  In the code sample supplied with this article, the below is implemented.  Also, I have included unit tests for all of these operatons, as dealing with money is a sensitive nature.

I will not be held liable for the use of these structs.  I do not ensure that all the structs or included classes innards work properly, however I am using them in production code so I feel confident about their use.

String Formatting

String formatting was a difficult problem to solve, and it is still not completely correct (but is usable) in my struct.  Currently, I use some primitive currency symbols (such as dollar sign, euro sign, yen sign, won sign, etc).  If a symbol is not available, the three-letter ISO code is used.  I have provided custom formatting options through a ToString(string format) overload, however it is limited.

Holding and Validating Currencies

After much debate, I decided to hardcode ISO standard currency-codes into the MoneyCurrency struct I created.  This will give you:

  • Three letter code
  • ISO numeric code
  • English name

You can access these currencies through readonly fields (MoneyCurrency.USDollar), or additionally there are lookup tables (MoneyCurrency.GetCurrency(isoCode)) to lookup the currencies you may require.

Operations (Addition, Subtraction, Etc.)

The Money struct supports various mathmatical functions such as addition, subtraction, division, multiplication, Min, Max and Absolute value functions.  These methods can be used like decimal values (operators are overloaded so "money1 + money2" is possible), and ensure currency-safe operatons.  A currency-safe operation is one that ensures two currencies cannot be operated on together (unless the scenario makes sense).  For example, you cannot add EUR10 + USD10, as an InvalidOperationException will be thrown.  This is very important for obvious reasons (data corruption).

If you wish to add or subtract values that are of different currencies, Money.ExchangeAndAdd and Money.ExchangeAndSubtract operations have been supplied.  These operations will use an ICurrencyExchanger (that you will need to supply yourself) to convert the currencies to a common currency and then add them.  If conversion is not necissary, the exchange will be ignored.

Comparison Operators

The comparison operators of the Money struct have been overloaded to allow comparisons between two Money.  The comparision operators are much more intelligent than you would expect, and ensure that a comparison can be made between the two values (sometimes two Money of different currencies should not logically be compared).  See the examples below:

Money eur10 = new Money(MoneyCurrency.Euro, 10);
Money eurNeg10 = new Money(MoneyCurrency.USDollar, -10);
Money usd10 = new Money(MoneyCurrency.USDollar, 10);
Money usdZero = new Money(MoneyCurrency.USDollar, 0);

bool result = (eur10 == usd10); // returns false;
bool result = (eur10 > usd10); // throws InvalidOperationException (comparison not valid)
bool result = (eur10 > Money.Zero); // returns true
bool result = (eur10 > usd0); // returns true
bool result = (usd10 > eurNeg10); // returns true (positive always greater than negative)

This will ensure that when comparisons can be made, they will occur, but comparisons that may not be possible will fail.  Note that equality comparisons do not throw exceptions if currencies are inequal, but rather return that they are not equal.

Unit Tests

The included unit tests were created with the VS.NET unit testing capabilities and run successfully.

Sample VS.NET 2005 Solution For Download Here

Leave a Comment

(required) 

(required) 

(optional)

(required)