Welcome back! Following on Part I and Part II of this series, let’s continue our look into some basic aspects of strings…

9. What are format strings?

Format strings are simply string representations of formatting patterns used when representing non-text data as text. For example, the same date might be written as “12 Dec 2012”, “12/12/12” or “2012-12-12”, depending on your locale and preference. In this example specific format strings may be used to ensure that the date always gets written in a certain way: “dd MMM yyyy”, “dd/mm/yy” or “yyyy-mm-dd”, respectively.

At a more generic level, formatting is the process of converting a data object (instance of a struct, class, or enum) to its string representation. If you’re thinking “ToString()”, then kudos on you! If you see “process of converting” and start thinking “serialization” then bring in the reigns somewhat 😉 While it is true that text formatting can be thought of as a simple form of serialization, more often than not it will result in a one-way transformation, insomuch as the reverse process is called “parsing” and not “deserialization” (big difference). It’s important to separate the two, since formatting/parsing is concerned specifically with text data, while serialization/deserialization is concerned with the bits.

In C#, there are a whole host of standard format strings and conventions for building custom format strings. Here are some useful shortcuts:
Standard Date and Time Format Strings
Custom Date and Time Format Strings
Standard Numeric Format Strings
Custom Numeric Format Strings
Standard TimeSpan Format Strings
Custom TimeSpan Format Strings
Enumeration Format Strings

There is also the String.Format() method in .Net, which accepts something called a Composite Format String. This is simply saying that the format string may contain a number of elements that will each be separately formatted based on their individual format strings.

Code:

string output
   = string.Format("Hello World!.. the time is now {0:%m} minutes after {0:%h} in the {0:tt}", 
                   DateTime.Now);
Console.WriteLine(output);

Output:

Hello World!.. the time is now 18 minutes after 12 in the PM

10. In what other ways can I format text output?

a. IFormatProvider and ICustomFormatter

IFormatProvider is a mechanism for delivering an object to control formatting. It has one method, object GetFormat( Type formatType ) that accepts an argument specifying the type of formatter the caller is requesting. Currently, formatType can be one of NumberFormatInfo, DateTimeFormatInfo, CultureInfo or ICustomFormatter. The implementing type should decide whether or not it can supply a formatter object of the requested type. If it can, it should do so (taking care that the object returned should be of the requested type). Otherwise, the format provider will return null. For most custom formatting scenarios, it follows that you would normally only be concerned with providing custom formatters in the form of ICustomFormatter implementations.

The typical pattern to supply custom formatting in this manner is to implement IFormatProvider and ICustomFormatter on the same type. The IFormatProvider implementation will then return itself (as an ICustomFormatter) when a caller requests a formatter of type ICustomFormatter and would return null for all other requested formatter types. The ICustomFormatter implementation will of course then be responsible for providing the actual formatting.

ICustomFormatter similarly has one method, string Format( string format, object arg, IFormatProvider formatProvider ) that accepts a format string, an object to format, and a format provider. The implementation should simply check that arg and formatProvider are valid and supported, and then use the format string supplied to decide how to format arg. For instance, one could expect a type called Temperature to support format strings “F”, “C” and “K” for formatting its text representation as Fahrenheit, Celcius or Kelvin, respectively.

The most immediate benefit of this implementation is that we can now supply our own format provider in a call to string.Format(IFormatProvider, string, object[]) and have a standardized method for creating text representations for our value objects.

b. IFormattable

IFormattable again has a single method, string ToString( string format, IFormatProvider formatProvider ), although it is a good convention to also supply as overloads string ToString( string format ) and an override of string ToString(), having both of these indirect back to the IFormattable implementation method. The actual implementation is then very similar to the one for ICustomFormatter above, except of course that arg is now represented by a strongly-typed this.

Some benefits of this method (apart from a simpler implementation) is that, whilst you retain the ability to use the formatter in string.Format() with composite format strings, you no longer need to supply a format provider yourself and (by extension) you are still free to use other standard format providers, like CultureInfo, to affect the formatting of your text. Also, you can now call directly into ToString(string) for specific formats.

c. TypeConverter

Although a little in-depth for this article (won’t go into the details here), it’s nevertheless a good place to mention TypeConverter to those who would like to explore it. Located in the System.ComponentModel namespace, the (inheritable) TypeConverter class, its descendants, and helpers such as TypeConverterAttribute, are typically used by UI components instead of ToString() for string formatting. It is not limited to string formatting, of course, but does specifically provide support for text-to-value conversion to assist property configuration at design time.