Click here to Skip to main content
13,897,535 members
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

14.1K views
18 bookmarked
Posted 14 Sep 2015
Licenced Ms-PL

Neat Tricks for Effortlessly Formatting Currency in C#

, 14 Sep 2015
Rate this:
Please Sign up or sign in to vote.
The article presents how to create a library of generic extension methods to effortlessly formatting currency in C#.

Introduction

In the article, I am going to present to you a few ways how to create a handy class that effortlessly formatting currency in C#. Initially, I came up with the idea because I needed to write several System/UI tests that needed to verify price amounts on the site that our team was developing. However, the currency formats on the different web forms were different, so I decided to write a utility for the job. I think it is worth sharing because it is taking advantage of several underutilized features of the C# language like Flags attribute, dynamic, generic and extension methods.

Formatting Currency Needs

All of the expected values of the tests were present in double or decimal, and I needed to format them to particular strings.

For example: $1,220.50, 1220.50$, -1220.5365$. Probably you can notice the few differences in the presented examples- the dollar sign is used as a prefix and as a suffix, the comma, the minus sign and the number precision. All strings had to be formatted using the en-us culture, we don’t have cases where a different currency is used.

My tests had to verify similar price amounts, discounts, and taxes, as depicted on the image.

Neat Tricks for Effortlessly Formatting Currency in C#

Combine Formatting Options Through Flags Attribute

As pointed previously we have a few possible scenarios for currency formatting. To be able to combine them I have created a special enum decorated with Flags attribute- DigitsFormattingSettings.

[Flags]
public enum DigitsFormattingSettings
{
    None = 0,
    NoComma = 1,
    PrefixDollar = 2,
    PrefixMinus = 4,
    SufixDollar = 8
} 

Flags enumerations are used for masking bit fields and doing bitwise comparisons. They are the correct design to use when multiple enumeration values can be specified at the same time.

It is also important to note that Flags does not automatically make the enum values powers of two. If you omit the numeric values, the enum will not work as one might expect in bitwise operations because the values by default start with 0 and increment.

Depending on the size of your enumeration, the exponential increase in values can get tricky to keep track of or calculate in your head. There is a technique in C# that will make this far more manageable. Through the use of bit shifting, we can move the 1 bit sequentially without having to worry about the actual integer value. It allows to see which bit switch you turned on for a particular value.

[Flags]
public enum DigitsFormattingSettingsBitShifting
{
    None = 0,
    NoComma = 1 << 0,
    PrefixDollar = 1 << 1,
    PrefixMinus = 1 << 2,
    SufixDollar = 1 << 3
}

Note: You cannot use the None enumerated constant in a bitwise AND operation to test for a flag because the result is always zero. However, you can perform a logical, not a bitwise, comparison between the numeric value and the None enumerated constant to determine whether any bits in the numeric value are set.

Official documentation– https://msdn.microsoft.com/en-us/library/ms229062.aspx

Extension ToString Methods for Currency Formatting

My main idea was to create a class holding multiple double and decimal extensions methods that provide the desired formatting capabilities. Initially, the primary method that handled the effortless formatting required a dynamic parameter (the number that was going to be formatted). Then the method was called internally from all public extension methods.

public static class CurrencyDelimiterFormatter
{
    private static readonly CultureInfo usCultureInfo = CultureInfo.CreateSpecificCulture("en-US");
        
    public static string ToStringUsDigitsFormatting(
        this double number,
        DigitsFormattingSettings digitsFormattingSettings = DigitsFormattingSettings.None,
        int precesion = 2)
    {
        string result = ToStringUsDigitsFormattingInternal(number, digitsFormattingSettings, precesion);

        return result;
    }
        
    public static string ToStringUsDigitsFormatting(
        this decimal number,
        DigitsFormattingSettings digitsFormattingSettings = DigitsFormattingSettings.None,
        int precesion = 2)
    {
        string result = ToStringUsDigitsFormattingInternal(number, digitsFormattingSettings, precesion);

        return result;
    }

    private static string ToStringUsDigitsFormattingInternal(
        dynamic number,
        DigitsFormattingSettings digitsFormattingSettings = DigitsFormattingSettings.None,
        int precesion = 2)
    {
        string formattedDigits = string.Empty;
        string currentNoComaFormatSpecifier = string.Concat("#.", new string('0', precesion));
        string currentComaFormatSpecifier = string.Concat("##,#.", new string('0', precesion));
        formattedDigits =
                            digitsFormattingSettings.HasFlag(DigitsFormattingSettings.NoComma) ? number.ToString(currentNoComaFormatSpecifier, usCultureInfo) :
                            number.ToString(currentComaFormatSpecifier, usCultureInfo);
        if (digitsFormattingSettings.HasFlag(DigitsFormattingSettings.PrefixDollar))
        {
            formattedDigits = string.Concat("$", formattedDigits);
        }
        if (digitsFormattingSettings.HasFlag(DigitsFormattingSettings.PrefixMinus))
        {
            formattedDigits = string.Concat("-", formattedDigits);
        }
        if (digitsFormattingSettings.HasFlag(DigitsFormattingSettings.SufixDollar))
        {
            formattedDigits = string.Concat(formattedDigits, "$");
        }
        return formattedDigits;
    }
}

I didn’t want to duplicate the formatting logic twice for double and decimal because of that I came up with this solution. But as you probably know variables of type dynamic are compiled into variables of type object. As a result of the boxing and unboxing between value types and object, the formatting was going to be a little bit slower compared to other alternatives.

Improved Generic Extension ToString Methods for Currency Formatting

As pointed above there are better alternatives of my initial implementation. One of them is the usage of generic methods.

private static string ToStringUsDigitsFormattingInternal<T>(
    T number,
    DigitsFormattingSettings digitsFormattingSettings = DigitsFormattingSettings.None,
    int precesion = 2)
    where T : struct,
                IComparable,
                IComparable<T>,
                IConvertible,
                IEquatable<T>,
                IFormattable
{
    string formattedDigits = string.Empty;
    string currentNoComaFormatSpecifier = string.Concat("#.", new string('0', precesion));
    string currentComaFormatSpecifier = string.Concat("##,#.", new string('0', precesion));
    formattedDigits =
                        digitsFormattingSettings.HasFlag(DigitsFormattingSettings.NoComma) ? number.ToString(currentNoComaFormatSpecifier, usCultureInfo) :
                        number.ToString(currentComaFormatSpecifier, usCultureInfo);
    if (digitsFormattingSettings.HasFlag(DigitsFormattingSettings.PrefixDollar))
    {
        formattedDigits = string.Concat("$", formattedDigits);
    }
    if (digitsFormattingSettings.HasFlag(DigitsFormattingSettings.PrefixMinus))
    {
        formattedDigits = string.Concat("-", formattedDigits);
    }
    if (digitsFormattingSettings.HasFlag(DigitsFormattingSettings.SufixDollar))
    {
        formattedDigits = string.Concat(formattedDigits, "$");
    }

    return formattedDigits;
}

The generic constraints assure that only value types can be passed as a generic parameter.

There are a couple of interesting parts of the code that require further explanations.

The ‘#’ custom specifier serves as a digit-placeholder symbol. If the value that is being formatted has a digit in the position where the “#” symbol appears in the format string, that number is copied to the result string. Otherwise, nothing is stored in that position in the result string.

The “.” custom format specifier inserts a localized decimal separator into the result string. The first period in the format string determines the location of the decimal separator in the formatted value; any additional periods are ignored.

The “,” character serves as both a group separator and a number scaling specifier.

The “0” custom format specifier serves as a zero-placeholder symbol. If the value that is being formatted has a digit in the position where the zero appears in the format string, that digit is copied to the result string; otherwise, a zero appears in the result string. The position of the leftmost zero before the decimal point and the rightmost zero after the decimal point determines the range of digits that are always present in the result string.

Through the precision parameter, we can specify a different count of the digits after the decimal point.

There are two main formats that the method uses, one for comma grouped numbers and one for the rest.

string currentNoComaFormatSpecifier = string.Concat("#.", new string('0', precesion));
string currentComaFormatSpecifier = string.Concat("##,#.", new string('0', precesion));

The zeroes for the specified precision are generated through the special string constructor that creates a new string from a specified character.

With the release of .NET 4.0, developers found a new method to handle the “dirty work” of checking to see if an enum has a flag. Every enum instance now has this method. You just pass the flag value you want to check for. I am using it to check if an additional formatting is needed as adding a dollar sign as a prefix/suffix, or the minus sign.

Formatting Currency in C# Example Usage

Usually, I set the namespace for my extension methods classes to be equal to the most basic one. In this example – CSharp.Series.Tests. This ease the usage of the extension methods around the project because no additional using statements are required.

public static unsafe void Main(string[] args)
{
    Console.WriteLine(1220.5.ToStringUsDigitsFormatting(DigitsFormattingSettings.PrefixDollar));
    // Result- $1,220.50
    Console.WriteLine(1220.5.ToStringUsDigitsFormatting(DigitsFormattingSettings.SufixDollar | DigitsFormattingSettings.NoComma));
    // Result- 1220.50$
    Console.WriteLine(1220.53645.ToStringUsDigitsFormatting(DigitsFormattingSettings.SufixDollar | DigitsFormattingSettings.NoComma | DigitsFormattingSettings.PrefixMinus, 4));
    // Result- -1220.5365$
}

The usage of the provided solution is straightforward. You can mix as many settings options as you like and specify a custom precision.

You are no more required to check every time the MSDN documentation for the ToString currency format options and hard code them at multiple places.

So Far in the C# Series

1. Implement Copy Paste C# Code
2. MSBuild TCP IP Logger C# Code
3. Windows Registry Read Write C# Code
4. Change .config File at Runtime C# Code
5. Generic Properties Validator C# Code
6. Reduced AutoMapper- Auto-Map Objects 180% Faster
7. 7 New Cool Features in C# 6.0
8. Types Of Code Coverage- Examples In C#
9. MSTest Rerun Failed Tests Through MSTest.exe Wrapper Application
10. Hints For Arranging Usings in Visual Studio Efficiently
11. 19 Must-Know Visual Studio Keyboard Shortcuts – Part 1
12. 19 Must-Know Visual Studio Keyboard Shortcuts – Part 2
13. Specify Assembly References Based On Build Configuration in Visual Studio
14. Top 15 Underutilized Features of .NET
15. Top 15 Underutilized Features of .NET Part 2
16. Neat Tricks for Effortlessly Format Currency in C#
17. Assert DateTime the Right Way MSTest NUnit C# Code
18. Which Works Faster- Null Coalescing Operator or GetValueOrDefault or Conditional Operator
19. Specification-based Test Design Techniques for Enhancing Unit Tests
20. Get Property Names Using Lambda Expressions in C#
21. Top 9 Windows Event Log Tips Using C#

 

If you enjoy my publications, feel free to SUBSCRIBE
Also, hit these share buttons. Thank you!

Source Code

Reference

The post Neat Tricks for Effortlessly Formatting Currency in C# appeared first on Automate The Planet.

All images are purchased from DepositPhotos.com and cannot be downloaded and used for free. License Agreement

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Anton Angelov
CEO Automate The Planet
Bulgaria Bulgaria
Anton Angelov is an IT Consultant and Quality Assurance Architect at Innovative Lab. He is passionate about automation testing and designing test harness and tools, having the best industry development practices in mind. In addition, he is an active blogger and the founder of Automate The Planet. He strives to make the site one of the leading authorities in Automation Testing by presenting compelling articles, inspiring ardent discussions amongst the community. He is also one of the most-rated-answer authors of questions about Test Automation Frameworks (WebDriver) on Stack Overflow.

You may also be interested in...

Comments and Discussions

 
QuestionRe Pin
mbb0115-Sep-15 2:20
membermbb0115-Sep-15 2:20 
AnswerRe: Re Pin
Anton Angelov15-Sep-15 10:01
memberAnton Angelov15-Sep-15 10:01 
QuestionWhy decide the format at run-time? Pin
John Brett15-Sep-15 1:19
memberJohn Brett15-Sep-15 1:19 
AnswerRe: Why decide the format at run-time? Pin
Anton Angelov15-Sep-15 10:02
memberAnton Angelov15-Sep-15 10:02 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web05 | 2.8.190306.1 | Last Updated 14 Sep 2015
Article Copyright 2015 by Anton Angelov
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid