Practical .NET

Supporting Multiple String Formats from Your Class

Developers (including you) benefit when you provide a string representation of your class. By implementing IFormattable, you can take control of this representation and provide some flexibility. Besides, if you don't provide one, the Microsoft .NET Framework will provide a useless one for you.

When anything in the Microsoft .NET Framework needs a string representation of your object, it calls your class's ToString method. If you add your object to a dropdown list, for example, the dropdown list will call your object's ToString method, inherited from System.Object, to get something the list can display on the screen. By default, the ToString method returns the name of your class inside curly braces: Your Customer class will return {MyNamespace.Customer}. So, if you add 25 Customer classes to a dropdown list, you get a dropdown list that displays {MyNamespace.Customer} 25 times. That's not very useful.

Not surprisingly, most developers override the ToString method so that it returns something that's actually useful. For the Customer object that would probably be the current values of the Id property or the FirstName and LastName properties. But, in something like the Customer object, there are often a variety of outputs that a developer might want from the ToString method. I can easily imagine that I might want the Customer object to identify itself with these combinations of its properties:

  • LastName, FirstName
  • FirstName MiddleInitial LastName
  • LastName, FirstName MiddleInitial
  • Salutation LastName

Developers typically end up cobbling these other combinations together using string concatenation. You can, however, support those options directly by having your object implement the IFormattable interface. If you want, you can even use this feature to provide more complex outputs -- there's no reason your ToString method couldn't hand back an XML version of your Customer object, for example.

Supporting Multiple Formats
The trick here is to have your class implement the IFormattable interface, which creates a ToString method that accepts a formatting code. For my Customer object and the outputs I listed earlier, I might support format strings like these:

  • L, F: LastName, FirstName
  • F M L: FirstName MiddleInitial LastName
  • L, FM: LastName, FirstName MiddleInitial
  • S L: Salutation LastName

The first step is to have your class implement the IFormattable interface:

Public Class Customer
  Implements IFormattable

End Class

That will cause Visual Studio to add a "formattable" version of the ToString method to your class. It looks something like this:

Public Function ToString1(format As String, formatProvider As IFormatProvider) As String Implements IFormattable.ToString

End Function

Now, it's just a matter of putting some code in that method. This example supports one of my formats and can be easily extended to support the rest:

Select Case format
  Case "F M L"
    Return Me.FirstName & " " & Me.MiddleName & " " & Me.LastName
End Select

With this method in place, developers can now use your format strings with your object anywhere that formatting strings are accepted. For example, all of these statements will work with this Customer object:

Dim cust As New Customer With {
                               .FirstName = "Peter",
                               .MiddleName = "Hunter",
                               .LastName = "Vogel"
String.Format("{0:F M L}", cust)
System.Diagnostics.Debug.Print("{0:F M L}", cust)
Console.WriteLine("{0:F M L}", cust)

If you're working with some .NET Framework component that has a Format property, you can now use your format codes in that property to control your class's output.

Covering the Defaults
If you do implement IFormattable, you're expected to handle the format code G (for "general") and the situation where no formatting string is supplied in code like this:

String.Format("{0}", cust) 

Essentially, this means that you need to pick one format to be your "default" format and use that for the G format and null/nothing. This example extends my code to use "LastName, FirstName" as the G/null/Nothing format (it also provides a Case Else to handle unknown format codes):

Select Case format
  Case "F M L"
    Return Me.FirstName & " " & Me.MiddleName & " " & Me.LastName
  Case "L, F", "G", Nothing
    Return Me.LastName & ", " & Me.FirstName
  Case Else
    Throw New FormatException("Unknown format code for Customer object")
End Select

Implementing the code in the IFormattable version of ToString does not, however, relieve you of the burden of overriding ToString itself -- if a developer calls the default ToString method directly, it will still hand back {MyNamespace.Customer}. The simplest way to handle this is still to override the default ToString method, but, within the method, call your IFormattable ToString method passing one of your format codes.

To call your IFormattable ToString method, you need to provide a value for the second parameter required by the IFormattable ToString method. That second parameter must be a FormatProvider class that handles formatting for the local culture (more technically, some class that implements the IFormatProvider interface). Fortunately, there are a number of places where you can get a FormatProvider (including System.Globalization.CultureInfor.CurrentCulture). Here, I'm passing the CurrentCulture property associated with the thread on which the code is executing:

Public Overrides Function ToString() As String
  Return Me.ToString1("G", Threading.Thread.CurrentThread.CurrentCulture)
End Function

Now, when developers use your class they won't have to concatenate properties together to display your object in a UI. Furthermore, you'll have control over all the acceptable representations of your object. I do like being in control.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at

comments powered by Disqus
Upcoming Events

.NET Insight

Sign up for our newsletter.

I agree to this site's Privacy Policy.