String formatting in .NET
26 January 2019
In any programming language, it is very common to want to take something that is not a string (say a number) and turn it into a string. Perhaps you need to display it on the command-line, or in some sort of GUI, or when writing to a file (etc …).
There are lots of different ways of turning .NET objects into a string, each with their own uses. I will try to show you as many as possible in this article.
All the examples will be in C#, because this is my blog and I can do what I like!
The ToString
method
Every object in .NET has a parameterless ToString
method (it is one of the few methods on the System.Object
base class). By default it returns the fully qualified name of the type (source).
This is usually not very useful, so the ToString
method is virtual to allow you to override it. When you implement any class in .NET, you can override ToString
to do whatever you want.
Many of the built-in .NET types override this method to do something useful. For example, all of the number types override ToString
to return a basically formatted number. Here are a couple of examples:
Type | Example | Output |
---|---|---|
System.Int32 |
-123456 |
-123456 |
System.Decimal |
-1.23m |
-1.23 |
Standard format strings
Many types in .NET have another overload of the ToString
method, which allows you to pass in what is known as a “format string”. This format string specifies how the object is to be formatted.
For example, if you call ToString
on a System.Decimal
with the format string "e"
, it will format it using exponential notation (e.g. 0.000123m
would be formatted as "1.23E-004"
).
The .NET documentation is pretty comprehensive when it comes to the standard format strings for numeric types and dates and times.
Custom format strings
The examples above are known as “standard” format strings, because they are pre-defined to control the behaviour of the ToString
method. However, sometimes it is useful to build up your own format strings from some pre-defined building blocks. These are known as “custom” format strings.
A good example of this is the System.DateTime
struct, where there are many building blocks, such as "d"
for the day of the month, "MMMM"
for the full name of the month and "yyyy"
for the full year.
Taking var date = new System.DateTime(2019, 1, 2)
as an example, here are some of the things you can do:
C# code | Output |
---|---|
date.ToString("dd MMM yyyy") |
02 Jan 2019 |
date.ToString("ddd d MMMM yyyy") |
Wed 2 January 2019 |
date.ToString("yyyy-MM-dd") |
2019-01-02 |
You’ll notice that anything that is not recognised as a building block (such as a space or hyphen) is simply output verbatim.
Cultures and IFormatProvider
All of the examples that we’ve seen so far have used English for any words (e.g. day and month names). This is because my computer is set up to work with British English, and .NET respects that by default. There are other formatting options that are particular to different cultures around the world; for example, in Europe it is customary to use a comma as a decimal separator rather than a full stop.
Many .NET types allow you to pass an instance of IFormatProvider
to the ToString
method which specifies how to deal with culture-specific formatting requirements. The CultureInfo
class implements this interface, and uses the standard .NET culture specifiers.
Here are some DateTime
examples, using the same example of var date = new System.DateTime(2019, 1, 2)
:
C# code | Output |
---|---|
date.ToString("d", new CultureInfo("en-GB")) |
Wednesday, 2 January 2019 |
date.ToString("d", new CultureInfo("en-US")) |
Wednesday, January 2, 2019 |
date.ToString("d", new CultureInfo("fr-FR")) |
mercredi 2 janvier 2019 |
date.ToString("d", new CultureInfo("de-DE")) |
Mittwoch, 2. Januar 2019 |
You can use CultureInfo.CurrentCulture
to access the machine’s culture, and Culture.InvariantCulture
to use a non-specific culture.
It is usually best to specify the culture, especially if you are interacting with another system that expects a value in a particular format.
Composing strings
Suppose you want to compose a string from many different building blocks. There are a number of different ways of doing this, so let’s take a look at these in turn.
For the following examples, let’s take the following values:
var amount = 5;
var price = 1.2m;
var date = new System.DateTime(2019, 1, 2);
String concatenation
The simplest way to compose strings is by using the +
operator:
var output = "We sold " + amount.ToString() + " mars bars on " + date.ToString("D", new CultureInfo("en-US")) + ", each costing £" + price.ToString("N2") + ".";
This gives "We sold 5 mars bars on Wednesday, January 2, 2019, each costing £1.20."
.
You can omit the ToString
call if you like, and .NET will do an implicit cast to string
, which is equivalent to calling ToString
with no arguments:
var output = "We sold " + amount + " mars bars on " + date + ", each costing £" + price + ".";
This gives "We sold 5 mars bars on 1/2/19 12:00:00 AM, each costing £1.2."
. As you can see, this is not always desirable as you have no control over how the strings are formatted.
The string.Format
method
The static method Format
on the System.String
class can be used to simplify this. You give it a string containing placeholders and some objects, and the method will format them and insert them into the placeholders.
It’s best explained with an example:
var output = string.Format("We sold {0} mars bars on {1}, each costing £{2}.", amount, date, price);
This gives "We sold 5 mars bars on 1/2/19 12:00:00 AM, each costing £1.2."
, as before. It has formatted the three objects by calling ToString
on them, and inserted them into the placeholders using zero-based indexing.
You can also specify format strings, like so:
var output = string.Format("We sold {0} mars bars on {1:D}, each costing £{2:N2}.", amount, date, price);
This gives "We sold 5 mars bars on Wednesday, January 2, 2019, each costing £1.20."
.
If you want to specify the culture, you can do so by passing it as the first parameter. This will use the specified culture for every conversion in the string. See here for more info.
Methods using string.Format
implicitly
One of the overloads to Console.WriteLine
has the following definition: public static void WriteLine(string format, params object[] arg)
. This works the same way as the string.Format
method: you give it a format string containing placeholders and some objects to be formatted and inserted.
There are a number of methods in the .NET framework and in third-party libraries that work this way, so watch out for them!
String interpolation
String interpolation is a feature that was introduced in C# 6, and it takes the string.Format
method one step further. If you prepend a string with $
, then anything inside curly braces gets formatted just like string.Format
. The following gives identical output to the previous example.
var output = $"We sold {amount} mars bars on {date:D}, each costing £{price:N2}.";
This can make your code much more readable, so I’d advise using it where you need to compose strings!
See here for more information, including how to specify the culture.
Debugging
When debugging, you often want to see the property values of a particular object. By default, the Visual Studio debug window will use the ToString
method to format the object so you can see its value.
You may be tempted to override ToString
to make debugging easier (remember, the default implementation of ToString
simply returns the type name). Don’t do this! If you do, it is very easy for these string representations to find their way into your production code. Instead, read this article to find out a better way of doing it using System.Diagnostics.DebuggerDisplayAttribute
.
Summary
In this article we saw a number of ways of formatting strings in .NET:
- The
ToString
method (with or without format strings and culture specifiers) - The
string.Format
method - String interpolation
Let me know if you can think of any other ways of doing this!