Formatting Types

Formatting is the process of converting an instance of a class, structure, or enumeration value to its string representation, often so that the resulting string can be displayed to users or deserialized to restore the original data type. This conversion can pose a number of challenges:

  • The way that values are stored internally does not necessarily reflect the way that users want to view them. For example, a telephone number might be stored in the form 8009999999, which is not user-friendly. It should instead be displayed as 800-999-9999.

  • Sometimes the conversion of an object to its string representation is not intuitive. For example, it is not clear how the string representation of a Temperature object or a Person object should appear.

  • Values often require culture-sensitive formatting. For example, in an application that uses numbers to reflect monetary values, numeric strings should include the current culture’s currency symbol, group separator (which, in most cultures, is the thousands separator), and decimal symbol.

  • An application may have to display the same value in different ways. For example, an application may represent an enumeration member by displaying a string representation of its name or by displaying its underlying value.

Note

Formatting converts the value of a type into a string representation. Parsing is the inverse of formatting. A parsing operation creates an instance of a data type from its string representation. For information about converting strings to other data types, see Parsing Strings.

The .NET Framework provides rich formatting support that enables developers to address these requirements.

This overview contains the following sections:

  • Formatting in the .NET Framework

  • Default Formatting Using the ToString Method

  • Overriding the ToString Method

  • The ToString Method and Format Strings

  • Format Providers and the IFormatProvider Interface

  • The IFormattable Interface

  • Composite Formatting

  • Custom Formatting with ICustomFormatter

  • Related Topics

  • Reference

Formatting in the .NET Framework

The basic mechanism for formatting is the default implementation of the Object.ToString method, which is discussed in the Default Formatting Using the ToString Method section later in this topic. However, the .NET Framework provides several ways to modify and extend its default formatting support. These include the following:

  • Overriding the Object.ToString method to define a custom string representation of an object’s value. For more information, see the Overriding the ToString Method section later in this topic.

  • Defining format specifiers that enable the string representation of an object’s value to take multiple forms. For example, the "X" format specifier in the following statement converts an integer to the string representation of a hexadecimal value.

    Dim integerValue As Integer = 60312
    Console.WriteLine(integerValue.ToString("X"))   ' Displays EB98.
    
    int integerValue = 60312;
    Console.WriteLine(integerValue.ToString("X"));   // Displays EB98.
    

    For more information about format specifiers, see the ToString Method and Format Strings section.

  • Using format providers to take advantage of the formatting conventions of a specific culture. For example, the following statement displays a currency value by using the formatting conventions of the en-US culture.

    Dim cost As Double = 1632.54
    Console.WriteLine(cost.ToString("C", New System.Globalization.CultureInfo("en-US")))
    ' The example displays the following output:
    '       $1,632.54
    
    double cost = 1632.54; 
    Console.WriteLine(cost.ToString("C", 
                      new System.Globalization.CultureInfo("en-US")));   
    // The example displays the following output:
    //       $1,632.54
    

    For more information about formatting with format providers, see the Format Providers and the IFormatProvider Interface section.

  • Implementing the IFormattable interface to support both string conversion with the Convert class and composite formatting. For more information, see the IFormattable Interface section.

  • Using composite formatting to embed the string representation of a value in a larger string. For more information, see the Composite Formatting section.

  • Implementing ICustomFormatter and IFormatProvider to provide a complete custom formatting solution. For more information, see the Custom Formatting with ICustomFormatter section.

The following sections examine these methods for converting an object to its string representation.

Back to top

Default Formatting Using the ToString Method

Every type that is derived from System.Object automatically inherits a parameterless ToString method, which returns the name of the type by default. The following example illustrates the default ToString method. It defines a class named Automobile that has no implementation. When the class is instantiated and its ToString method is called, it displays its type name.

Public Class Automobile
   ' No implementation. All members are inherited from Object.
End Class

Module Example
   Public Sub Main()
      Dim firstAuto As New Automobile()
      Console.WriteLine(firstAuto)
   End Sub
End Module
' The example displays the following output:
'       Automobile
using System;

public class Automobile
{
   // No implementation. All members are inherited from Object.
}

public class Example
{
   public static void Main()
   {
      Automobile firstAuto = new Automobile();
      Console.WriteLine(firstAuto);
   }
}
// The example displays the following output:
//       Automobile

Because all types other than interfaces are derived from Object, this functionality is automatically provided to your custom classes or structures. However, the functionality offered by the default ToString method, is limited: Although it identifies the type, it fails to provide any information about an instance of the type. To provide a string representation of an object that provides information about that object, you must override the ToString method.

Note

Structures inherit from ValueType, which in turn is derived from Object. Although ValueType overrides Object.ToString, its implementation is identical.

Back to top

Overriding the ToString Method

Displaying the name of a type is often of limited use and does not allow consumers of your types to differentiate one instance from another. However, you can override the ToString method to provide a more useful representation of an object’s value. The following example defines a Temperature object and overrides its ToString method to display the temperature in degrees Celsius.

Public Class Temperature
   Private temp As Decimal

   Public Sub New(temperature As Decimal)
      Me.temp = temperature
   End Sub

   Public Overrides Function ToString() As String
      Return Me.temp.ToString("N1") + "°C"   
   End Function
End Class

Module Example
   Public Sub Main()
      Dim currentTemperature As New Temperature(23.6d)
      Console.WriteLine("The current temperature is {0}.", currentTemperature)
   End Sub
End Module
' The example displays the following output:
'       The current temperature is 23.6°C.
using System;

public class Temperature
{
   private decimal temp;

   public Temperature(decimal temperature)
   {
      this.temp = temperature;   
   }

   public override string ToString()
   {
      return this.temp.ToString("N1") + "°C";
   }
}

public class Example
{
   public static void Main()
   {
      Temperature currentTemperature = new Temperature(23.6m);
      Console.WriteLine("The current temperature is {0}.", currentTemperature);
   }
}
// The example displays the following output:
//       The current temperature is 23.6°C.

In the .NET Framework, the ToString method of each primitive value type has been overridden to display the object’s value instead of its name. The following table shows the override for each primitive type. Note that most of the overridden methods call another overload of the ToString method and pass it the "G" format specifier, which defines the general format for its type, and an IFormatProvider object that represents the current culture.

Type

ToString override

Boolean

Returns either Boolean.TrueString or Boolean.FalseString.

Byte

Calls Byte.ToString("G", NumberFormatInfo.CurrentInfo) to format the Byte value for the current culture.

Char

Returns the character as a string.

DateTime

Calls DateTime.ToString("G", DatetimeFormatInfo.CurrentInfo) to format the date and time value for the current culture.

Decimal

Calls Decimal.ToString("G", NumberFormatInfo.CurrentInfo) to format the Decimal value for the current culture.

Double

Calls Double.ToString("G", NumberFormatInfo.CurrentInfo) to format the Double value for the current culture.

Int16

Calls Int16.ToString("G", NumberFormatInfo.CurrentInfo) to format the Int16 value for the current culture.

Int32

Calls Int32.ToString("G", NumberFormatInfo.CurrentInfo) to format the Int32 value for the current culture.

Int64

Calls Int64.ToString("G", NumberFormatInfo.CurrentInfo) to format the Int64 value for the current culture.

SByte

Calls SByte.ToString("G", NumberFormatInfo.CurrentInfo) to format the SByte value for the current culture.

Single

Calls Single.ToString("G", NumberFormatInfo.CurrentInfo) to format the Single value for the current culture.

UInt16

Calls UInt16.ToString("G", NumberFormatInfo.CurrentInfo) to format the UInt16 value for the current culture.

UInt32

Calls UInt32.ToString("G", NumberFormatInfo.CurrentInfo) to format the UInt32 value for the current culture.

UInt64

Calls UInt64.ToString("G", NumberFormatInfo.CurrentInfo) to format the UInt64 value for the current culture.

Back to top

The ToString Method and Format Strings

Relying on the default ToString method or overriding ToString is appropriate when an object has a single string representation. However, the value of an object often has multiple representations. For example, a temperature can be expressed in degrees Fahrenheit, degrees Celsius, or kelvins. Similarly, the integer value 10 can be represented in numerous ways, including 10, 10.0, 1.0e01, or $10.00.

To enable a single value to have multiple string representations, the .NET Framework uses format strings. A format string is a string that contains one or more predefined format specifiers, which are single characters or groups of characters that define how the ToString method should format its output. The format string is then passed as a parameter to the object's ToString method and determines how the string representation of that object's value should appear.

All numeric types, date and time types, and enumeration types in the .NET Framework support a predefined set of format specifiers. You can also use format strings to define multiple string representations of your application-defined data types.

Standard Format Strings

A standard format string contains a single format specifier, which is an alphabetic character that defines the string representation of the object to which it is applied, along with an optional precision specifier that affects how many digits are displayed in the result string. If the precision specifier is omitted or is not supported, a standard format specifier is equivalent to a standard format string.

The .NET Framework defines a set of standard format specifiers for all numeric types, all date and time types, and all enumeration types. For example, each of these categories supports a "G" standard format specifier, which defines a general string representation of a value of that type.

Standard format strings for enumeration types directly control the string representation of a value. The format strings passed to an enumeration value’s ToString method determine whether the value is displayed using its string name (the "G" and "F" format specifiers), its underlying integral value (the "D" format specifier), or its hexadecimal value (the "X" format specifier). The following example illustrates the use of standard format strings to format a DayOfWeek enumeration value.

Dim thisDay As DayOfWeek = DayOfWeek.Monday
Dim formatStrings() As String = {"G", "F", "D", "X"}

For Each formatString As String In formatStrings
   Console.WriteLine(thisDay.ToString(formatString))
Next
' The example displays the following output:
'       Monday
'       Monday
'       1
'       00000001
DayOfWeek thisDay = DayOfWeek.Monday;
string[] formatStrings = {"G", "F", "D", "X"};

foreach (string formatString in formatStrings)
   Console.WriteLine(thisDay.ToString(formatString));
// The example displays the following output:
//       Monday
//       Monday
//       1
//       00000001

For information about enumeration format strings, see Enumeration Format Strings.

Standard format strings for numeric types usually define a result string whose precise appearance is controlled by one or more property values. For example, the "C" format specifier formats a number as a currency value. When you call the ToString method with the "C" format specifier as the only parameter, the following property values from the current culture’s NumberFormatInfo object are used to define the string representation of the numeric value:

  • The CurrencySymbol property, which specifies the current culture’s currency symbol.

  • The CurrencyNegativePattern or CurrencyPositivePattern property, which returns an integer that determines the following:

    • The placement of the currency symbol.

    • Whether negative values are indicated by a leading negative sign, a trailing negative sign, or parentheses.

    • Whether a space appears between the numeric value and the currency symbol.

  • The CurrencyDecimalDigits property, which defines the number of fractional digits in the result string.

  • The CurrencyDecimalSeparator property, which defines the decimal separator symbol in the result string.

  • The CurrencyGroupSeparator property, which defines the group separator symbol.

  • The CurrencyGroupSizes property, which defines the number of digits in each group to the left of the decimal.

  • The NegativeSign property, which determines the negative sign used in the result string if parentheses are not used to indicate negative values.

In addition, numeric format strings may include a precision specifier. The meaning of this specifier depends on the format string with which it is used, but it typically indicates either the total number of digits or the number of fractional digits that should appear in the result string. For example, the following example uses the "X4" standard numeric string and a precision specifier to create a string value that has four hexadecimal digits.

Dim byteValues() As Byte = { 12, 163, 255 }
For Each byteValue As Byte In byteValues
   Console.WriteLine(byteValue.ToString("X4"))
Next
' The example displays the following output:
'       000C
'       00A3
'       00FF
byte[] byteValues = { 12, 163, 255 };
foreach (byte byteValue in byteValues)
   Console.WriteLine(byteValue.ToString("X4"));
// The example displays the following output:
//       000C
//       00A3
//       00FF

For more information about standard numeric formatting strings, see Standard Numeric Format Strings.

Standard format strings for date and time values are aliases for custom format strings stored by a particular DateTimeFormatInfo property. For example, calling the ToString method of a date and time value with the "D" format specifier displays the date and time by using the custom format string stored in the current culture’s DateTimeFormatInfo.LongDatePattern property. (For more information about custom format strings, see the next section.) The following example illustrates this relationship.

Imports System.Globalization

Module Example
   Public Sub Main()
      Dim date1 As Date = #6/30/2009#
      Console.WriteLine("D Format Specifier:     {0:D}", date1)
      Dim longPattern As String = CultureInfo.CurrentCulture.DateTimeFormat.LongDatePattern
      Console.WriteLine("'{0}' custom format string:     {1}", _
                        longPattern, date1.ToString(longPattern))
   End Sub
End Module
' The example displays the following output when run on a system whose
' current culture is en-US:
'    D Format Specifier:     Tuesday, June 30, 2009
'    'dddd, MMMM dd, yyyy' custom format string:     Tuesday, June 30, 2009
using System;
using System.Globalization;

public class Example
{
   public static void Main()
   {
      DateTime date1 = new DateTime(2009, 6, 30);
      Console.WriteLine("D Format Specifier:     {0:D}", date1);
      string longPattern = CultureInfo.CurrentCulture.DateTimeFormat.LongDatePattern;
      Console.WriteLine("'{0}' custom format string:     {1}", 
                        longPattern, date1.ToString(longPattern));
   }
}
// The example displays the following output when run on a system whose
// current culture is en-US:
//    D Format Specifier:     Tuesday, June 30, 2009
//    'dddd, MMMM dd, yyyy' custom format string:     Tuesday, June 30, 2009

For more information about standard date and time format strings, see Standard Date and Time Format Strings.

You can also use standard format strings to define the string representation of an application-defined object that is produced by the object’s ToString(String) method. You can define the specific standard format specifiers that your object supports, and you can determine whether they are case-sensitive or case-insensitive. Your implementation of the ToString(String) method should support the following:

  • A "G" format specifier that represents a customary or common format of the object. The parameterless overload of your object's ToString method should call its ToString(String) overload and pass it the "G" standard format string.

  • Support for a format specifier that is equal to a null reference (Nothing in Visual Basic). A format specifier that is equal to a null reference should be considered equivalent to the "G" format specifier.

For example, a Temperature class can internally store the temperature in degrees Celsius and use format specifiers to represent the value of the Temperature object in degrees Celsius, degrees Fahrenheit, and kelvins. The following example provides an illustration.

Public Class Temperature
   Private m_Temp As Decimal

   Public Sub New(temperature As Decimal)
      Me.m_Temp = temperature
   End Sub

   Public ReadOnly Property Celsius() As Decimal
      Get
         Return Me.m_Temp
      End Get   
   End Property

   Public ReadOnly Property Kelvin() As Decimal
      Get
         Return Me.m_Temp + 273.15d   
      End Get
   End Property

   Public ReadOnly Property Fahrenheit() As Decimal
      Get
         Return Math.Round(CDec(Me.m_Temp * 9 / 5 + 32), 2)
      End Get      
   End Property

   Public Overrides Function ToString() As String
      Return Me.ToString("C")
   End Function

   Public Overloads Function ToString(format As String) As String  
      ' Handle null or empty string.
      If String.IsNullOrEmpty(format) Then format = "C"
      ' Remove spaces and convert to uppercase.
      format = format.Trim().ToUpperInvariant()      

      ' Convert temperature to Fahrenheit and return string.
      Select Case format
         Case "F"
            Return Me.Fahrenheit.ToString("N2") & " °F"
         ' Convert temperature to Kelvin and return string.
         Case "K"
            Return Me.Kelvin.ToString("N2") & " K"
         ' Return temperature in Celsius.
         Case "C"
            Return Me.Celsius.ToString("N2") & " °C"
         Case Else
            Throw New FormatException(String.Format("The '{0}' format string is not supported.", format))
      End Select      
   End Function
End Class

Public Module Example
   Public Sub Main()
      Dim temp1 As New Temperature(0d)
      Console.WriteLine(temp1.ToString())
      Console.WriteLine(temp1.ToString("C"))
      Console.WriteLine(temp1.ToString("F"))
      Console.WriteLine(temp1.ToString("K"))
      Dim temp2 As New Temperature(-40d)
      Console.WriteLine(temp2.ToString())
      Console.WriteLine(temp2.ToString("C"))
      Console.WriteLine(temp2.ToString("F"))
      Console.WriteLine(temp2.ToString("K"))
      Dim temp3 As New Temperature(16d)
      Console.WriteLine(temp3.ToString())
      Console.WriteLine(temp3.ToString("C"))
      Console.WriteLine(temp3.ToString("F"))
      Console.WriteLine(temp3.ToString("K"))

      Console.WriteLine(String.Format("The temperature is now {0:F}.", temp3))
   End Sub
End Module
' The example displays the following output:
'       0.00 °C
'       0.00 °C
'       32.00 °F
'       273.15 K
'       -40.00 °C
'       -40.00 °C
'       -40.00 °F
'       233.15 K
'       16.00 °C
'       16.00 °C
'       60.80 °F
'       289.15 K
'       The temperature is now 16.00 °C.
public class Temperature
{
   private decimal m_Temp;

   public Temperature(decimal temperature)
   {
      this.m_Temp = temperature;
   }

   public decimal Celsius
   {
      get { return this.m_Temp; }
   }

   public decimal Kelvin
   {
      get { return this.m_Temp + 273.15m; }   
   }

   public decimal Fahrenheit
   {
      get { return Math.Round(((decimal) (this.m_Temp * 9 / 5 + 32)), 2); }
   }

   public override string ToString()
   {
      return this.ToString("C");
   }

   public string ToString(string format)
   {  
      // Handle null or empty string.
      if (String.IsNullOrEmpty(format)) format = "C";
      // Remove spaces and convert to uppercase.
      format = format.Trim().ToUpperInvariant();      

      // Convert temperature to Fahrenheit and return string.
      switch (format)
      {
         // Convert temperature to Fahrenheit and return string.
         case "F":
            return this.Fahrenheit.ToString("N2") + " °F";
         // Convert temperature to Kelvin and return string.
         case "K":
            return this.Kelvin.ToString("N2") + " K";
         // return temperature in Celsius.
         case "C":
            return this.Celsius.ToString("N2") + " °C";
         default:
            throw new FormatException(String.Format("The '{0}' format string is not supported.", format));
      }      
   }
}

public class Example
{
   public static void Main()
   {
      Temperature temp1 = new Temperature(0m);
      Console.WriteLine(temp1.ToString());
      Console.WriteLine(temp1.ToString("C"));
      Console.WriteLine(temp1.ToString("F"));
      Console.WriteLine(temp1.ToString("K"));
      Temperature temp2 = new Temperature(-40m);
      Console.WriteLine(temp2.ToString());
      Console.WriteLine(temp2.ToString("C"));
      Console.WriteLine(temp2.ToString("F"));
      Console.WriteLine(temp2.ToString("K"));
      Temperature temp3 = new Temperature(16m);
      Console.WriteLine(temp3.ToString());
      Console.WriteLine(temp3.ToString("C"));
      Console.WriteLine(temp3.ToString("F"));
      Console.WriteLine(temp3.ToString("K"));

      Console.WriteLine(String.Format("The temperature is now {0:F}.", temp3));
   }
}
// The example displays the following output:
//       0.00 °C
//       0.00 °C
//       32.00 °F
//       273.15 K
//       -40.00 °C
//       -40.00 °C
//       -40.00 °F
//       233.15 K
//       16.00 °C
//       16.00 °C
//       60.80 °F
//       289.15 K
//       The temperature is now 16.00 °C.

Back to top

Custom Format Strings

In addition to the standard format strings, the .NET Framework defines custom format strings for both numeric values and date and time values. A custom format string consists of one or more custom format specifiers that define the string representation of a value. For example, the custom date and time format string "yyyy/mm/dd hh:mm:ss.ffff t zzz" converts a date to its string representation in the form "2008/11/15 07:45:00.0000 P -08:00" for the en-US culture. Similarly, the custom format string "0000" converts the integer value 12 to "0012". For a complete list of custom format strings, see Custom Date and Time Format Strings and Custom Numeric Format Strings.

If a format string consists of a single custom format specifier, the format specifier should be preceded by the percent (%) symbol to avoid confusion with a standard format specifier. The following example uses the "M" custom format specifier to display a one-digit or two-digit number of the month of a particular date.

Dim date1 As Date = #09/08/2009#
Console.WriteLine(date1.ToString("%M"))      ' Displays 9
DateTime date1 = new DateTime(2009, 9, 8);
Console.WriteLine(date1.ToString("%M"));       // Displays 9

Many standard format strings for date and time values are aliases for custom format strings that are defined by properties of the DateTimeFormatInfo object. Custom format strings also offer considerable flexibility in providing application-defined formatting for numeric values or date and time values. You can define your own custom result strings for both numeric values and date and time values by combining multiple custom format specifiers into a single custom format string. The following example defines a custom format string that displays the day of the week in parentheses after the month name, day, and year.

Dim customFormat As String = "MMMM dd, yyyy (dddd)"
Dim date1 As Date = #8/28/2009#
Console.WriteLine(date1.ToString(customFormat))   
' The example displays the following output if run on a system
' whose language is English:
'       August 28, 2009 (Friday)      
string customFormat = "MMMM dd, yyyy (dddd)";
DateTime date1 = new DateTime(2009, 8, 28);
Console.WriteLine(date1.ToString(customFormat));   
// The example displays the following output if run on a system
// whose language is English:
//       August 28, 2009 (Friday)      

Although standard format strings can generally handle most of the formatting needs for your application-defined types, you may also define custom format specifiers to format your types.

Back to top

Format Providers and the IFormatProvider Interface

Although format specifiers let you customize the formatting of objects, producing a meaningful string representation of objects often requires additional formatting information. For example, formatting a number as a currency value by using either the "C" standard format string or a custom format string such as "$ #,#.00" requires, at a minimum, information about the correct currency symbol, group separator, and decimal separator to be available to include in the formatted string. In the .NET Framework, this additional formatting information is made available through the IFormatProvider interface, which is provided as a parameter to one or more overloads of the ToString method of numeric types and date and time types. The following example illustrates how the string representation of an object changes when it is formatted with three different IFormatProvider objects.

Imports System.Globalization

Public Module Example
   Public Sub Main()
      Dim value As Decimal = 1603.42d
      Console.WriteLine(value.ToString("C3", New CultureInfo("en-US")))
      Console.WriteLine(value.ToString("C3", New CultureInfo("fr-FR")))
      Console.WriteLine(value.ToString("C3", New CultureInfo("de-DE")))
   End Sub
End Module
' The example displays the following output:
'       $1,603.420
'       1 603,420 €
'       1.603,420 €
using System;
using System.Globalization;

public class Example
{
   public static void Main()
   {
      decimal value = 1603.42m;
      Console.WriteLine(value.ToString("C3", new CultureInfo("en-US")));
      Console.WriteLine(value.ToString("C3", new CultureInfo("fr-FR")));
      Console.WriteLine(value.ToString("C3", new CultureInfo("de-DE")));
   }
}
// The example displays the following output:
//       $1,603.420
//       1 603,420 €
//       1.603,420 €

The IFormatProvider interface includes one method, GetFormat(Type), which has a single parameter that specifies the type of object that provides formatting information. If the method can provide an object of that type, it returns it. Otherwise, it returns a null reference (Nothing in Visual Basic).

IFormatProvider.GetFormat is a callback method. When you call a ToString method overload that includes an IFormatProvider parameter, it calls the GetFormat method of that IFormatProvider object. The GetFormat method is responsible for returning an object that provides the necessary formatting information, as specified by its formatType parameter, to the ToString method.

A number of formatting or string conversion methods include a parameter of type IFormatProvider, but in many cases the value of the parameter is ignored when the method is called. The following table lists some of the formatting methods that use the parameter and the type of the Type object that they pass to the IFormatProvider.GetFormat method.

Method

Type of formatType parameter

ToString method of numeric types

System.Globalization.NumberFormatInfo

ToString method of date and time types

System.Globalization.DateTimeFormatInfo

String.Format

System.ICustomFormatter

StringBuilder.AppendFormat

System.ICustomFormatter

Note

The ToString methods of the numeric types and date and time types are overloaded, and only some of the overloads include an IFormatProvider parameter. If a method does not have a parameter of type IFormatProvider, the object that is returned by the CultureInfo.CurrentCulture property is passed instead. For example, a call to the default Int32.ToString() method ultimately results in a method call such as the following: Int32.ToString("G", System.Globalization.CultureInfo.CurrentCulture).

The .NET Framework provides three classes that implement IFormatProvider:

You can also implement your own format provider to replace any one of these classes. However, your implementation’s GetFormat method must return an object of the type listed in the previous table if it has to provide formatting information to the ToString method.

Back to top

The IFormattable Interface

Typically, types that overload the ToString method with a format string and an IFormatProvider parameter also implement the IFormattable interface. This interface has a single member, IFormattable.ToString(String, IFormatProvider), that includes both a format string and a format provider as parameters.

Implementing the IFormattable interface for your application-defined class offers two advantages:

The following example defines a Temperature class that implements the IFormattable interface. It supports the "C" or "G" format specifiers to display the temperature in Celsius, the "F" format specifier to display the temperature in Fahrenheit, and the "K" format specifier to display the temperature in Kelvin.

Imports System.Globalization

Public Class Temperature : Implements IFormattable
   Private m_Temp As Decimal

   Public Sub New(temperature As Decimal)
      Me.m_Temp = temperature
   End Sub

   Public ReadOnly Property Celsius() As Decimal
      Get
         Return Me.m_Temp
      End Get   
   End Property

   Public ReadOnly Property Kelvin() As Decimal
      Get
         Return Me.m_Temp + 273.15d   
      End Get
   End Property

   Public ReadOnly Property Fahrenheit() As Decimal
      Get
         Return Math.Round(CDec(Me.m_Temp * 9 / 5 + 32), 2)
      End Get      
   End Property

   Public Overrides Function ToString() As String
      Return Me.ToString("G", Nothing)
   End Function

   Public Overloads Function ToString(format As String) As String
      Return Me.ToString(format, Nothing)
   End Function

   Public Overloads Function ToString(format As String, provider As IFormatProvider) As String _  
      Implements IFormattable.ToString

      ' Handle null or empty arguments.
      If String.IsNullOrEmpty(format) Then format = "G"
      ' Remove any white space and convert to uppercase.
      format = format.Trim().ToUpperInvariant()

      If provider Is Nothing Then provider = NumberFormatInfo.CurrentInfo

      Select Case format
         ' Convert temperature to Fahrenheit and return string.
         Case "F"
            Return Me.Fahrenheit.ToString("N2", provider) & "°F"
         ' Convert temperature to Kelvin and return string.
         Case "K"
            Return Me.Kelvin.ToString("N2", provider) & "K"
         ' Return temperature in Celsius.
         Case "C", "G"
            Return Me.Celsius.ToString("N2", provider) & "°C"
         Case Else
            Throw New FormatException(String.Format("The '{0}' format string is not supported.", format))
      End Select      
   End Function
End Class
using System;
using System.Globalization;

public class Temperature : IFormattable
{
   private decimal m_Temp;

   public Temperature(decimal temperature)
   {
      this.m_Temp = temperature;
   }

   public decimal Celsius
   {
      get { return this.m_Temp; }
   }

   public decimal Kelvin
   {
      get { return this.m_Temp + 273.15m; }   
   }

   public decimal Fahrenheit
   {
      get { return Math.Round((decimal) this.m_Temp * 9 / 5 + 32, 2); }
   }

   public override string ToString()
   {
      return this.ToString("G", null);
   }

   public string ToString(string format)
   {
      return this.ToString(format, null);
   }

   public string ToString(string format, IFormatProvider provider)  
   {
      // Handle null or empty arguments.
      if (String.IsNullOrEmpty(format)) format = "G";
      // Remove any white space and convert to uppercase.
      format = format.Trim().ToUpperInvariant();

      if (provider == null) provider = NumberFormatInfo.CurrentInfo;

      switch (format)
      {
         // Convert temperature to Fahrenheit and return string.
         case "F":
            return this.Fahrenheit.ToString("N2", provider) + "°F";
         // Convert temperature to Kelvin and return string.
         case "K":
            return this.Kelvin.ToString("N2", provider) + "K";
         // Return temperature in Celsius.
         case "C":
         case "G":
            return this.Celsius.ToString("N2", provider) + "°C";
         default:
            throw new FormatException(String.Format("The '{0}' format string is not supported.", format));
      }      
   }
}

The following example instantiates a Temperature object. It then calls the ToString method and uses several composite format strings to obtain different string representations of a Temperature object. Each of these method calls, in turn, calls the IFormattable implementation of the Temperature class.

Public Module Example
   Public Sub Main()
      Dim temp1 As New Temperature(22d)
      Console.WriteLine(Convert.ToString(temp1, New CultureInfo("ja-JP")))
      Console.WriteLine("Temperature: {0:K}", temp1)
      Console.WriteLine("Temperature: {0:F}", temp1)
      Console.WriteLine(String.Format(New CultureInfo("fr-FR"), "Temperature: {0:F}", temp1)) 
   End Sub
End Module
' The example displays the following output:
'       22.00°C
'       Temperature: 295.15°K
'       Temperature: 71.60°F
'       Temperature: 71,60°F
public class Example
{
   public static void Main()
   {
      Temperature temp1 = new Temperature(22m);
      Console.WriteLine(Convert.ToString(temp1, new CultureInfo("ja-JP")));
      Console.WriteLine("Temperature: {0:K}", temp1);
      Console.WriteLine("Temperature: {0:F}", temp1);
      Console.WriteLine(String.Format(new CultureInfo("fr-FR"), "Temperature: {0:F}", temp1));
   }
}
// The example displays the following output:
//       22.00°C
//       Temperature: 295.15°K
//       Temperature: 71.60°F
//       Temperature: 71,60°F

Back to top

Composite Formatting

Some methods, such as String.Format and StringBuilder.AppendFormat, support composite formatting. A composite format string is a kind of template that returns a single string that incorporates the string representation of zero, one, or more objects. Each object is represented in the composite format string by an indexed format item. The index of the format item corresponds to the position of the object that it represents in the method's parameter list. Indexes are zero-based. For example, in the following call to the String.Format method, the first format item, {0:D}, is replaced by the string representation of thatDate; the second format item, {1}, is replaced by the string representation of item1; and the third format item, {2:C2}, is replaced by the string representation of item1.Value.

result = String.Format("On {0:d}, the inventory of {1} was worth {2:C2}.", _
                       thatDate, item1, item1.Value)
Console.WriteLine(result)                            
' The example displays output like the following if run on a system
' whose current culture is en-US:
'       On 5/1/2009, the inventory of WidgetA was worth $107.44.
result = String.Format("On {0:d}, the inventory of {1} was worth {2:C2}.", 
                       thatDate, item1, item1.Value);
Console.WriteLine(result);                            
// The example displays output like the following if run on a system
// whose current culture is en-US:
//       On 5/1/2009, the inventory of WidgetA was worth $107.44.

For more information about composite formatting, see Composite Formatting.

Back to top

Custom Formatting with ICustomFormatter

Some composite formatting methods, such as String.Format(IFormatProvider, String, Object[]) and StringBuilder.AppendFormat(IFormatProvider, String, Object[]),include a format provider parameter that supports custom formatting. When the formatting method is called, it passes a Type object that represents an ICustomFormatter interface to the format provider’s GetFormat method. The GetFormat method is then responsible for returning the ICustomFormatter implementation that provides custom formatting.

The ICustomFormatter interface has a single method, Format(String, Object, IFormatProvider), that is called automatically by a composite formatting method, once for each format item in a composite format string. The Format(String, Object, IFormatProvider) method has three parameters: a format string, which represents the formatString argument in a format item, an object to format, and an IFormatProvider object that provides formatting services. Typically, the class that implements ICustomFormatter also implements IFormatProvider, so this last parameter is a reference to the custom formatting class itself. The method returns a custom formatted string representation of the object to be formatted. If the method cannot format the object, it should return a null reference (Nothing in Visual Basic).

The following example provides an ICustomFormatter implementation named ByteByByteFormatter that displays integer values as a sequence of two-digit hexadecimal values followed by a space.

Public Class ByteByByteFormatter : Implements IFormatProvider, ICustomFormatter
   Public Function GetFormat(formatType As Type) As Object _
                   Implements IFormatProvider.GetFormat
      If formatType Is GetType(ICustomFormatter) Then
         Return Me
      Else
         Return Nothing
      End If
   End Function

   Public Function Format(fmt As String, arg As Object, 
                          formatProvider As IFormatProvider) As String _
                          Implements ICustomFormatter.Format

      If Not formatProvider.Equals(Me) Then Return Nothing

      ' Handle only hexadecimal format string.
      If Not fmt.StartsWith("X") Then 
            Return Nothing
      End If

      ' Handle only integral types.
      If Not typeof arg Is Byte AndAlso
         Not typeof arg Is Int16 AndAlso
         Not typeof arg Is Int32 AndAlso
         Not typeof arg Is Int64 AndAlso
         Not typeof arg Is SByte AndAlso
         Not typeof arg Is UInt16 AndAlso
         Not typeof arg Is UInt32 AndAlso
         Not typeof arg Is UInt64 Then _
            Return Nothing

      Dim bytes() As Byte = BitConverter.GetBytes(arg)
      Dim output As String = Nothing

      For ctr As Integer = bytes.Length - 1 To 0 Step -1
         output += String.Format("{0:X2} ", bytes(ctr))   
      Next

      Return output.Trim()
   End Function
End Class
public class ByteByByteFormatter : IFormatProvider, ICustomFormatter
{
   public object GetFormat(Type formatType)
   { 
      if (formatType == typeof(ICustomFormatter))
         return this;
      else
         return null;
   }

   public string Format(string format, object arg, 
                          IFormatProvider formatProvider)
   {   
      if (! formatProvider.Equals(this)) return null;

      // Handle only hexadecimal format string.
      if (! format.StartsWith("X")) return null;

      byte[] bytes;
      string output = null;

      // Handle only integral types.
      if (arg is Byte) 
         bytes = BitConverter.GetBytes((Byte) arg);
      else if (arg is Int16)
         bytes = BitConverter.GetBytes((Int16) arg);
      else if (arg is Int32)
         bytes = BitConverter.GetBytes((Int32) arg);
      else if (arg is Int64)   
         bytes = BitConverter.GetBytes((Int64) arg);
      else if (arg is SByte)
         bytes = BitConverter.GetBytes((SByte) arg);
      else if (arg is UInt16)
         bytes = BitConverter.GetBytes((UInt16) arg);
      else if (arg is UInt32)
         bytes = BitConverter.GetBytes((UInt32) arg);
      else if (arg is UInt64)
         bytes = BitConverter.GetBytes((UInt64) arg);
      else
         return null;

      for (int ctr = bytes.Length - 1; ctr >= 0; ctr--)
         output += String.Format("{0:X2} ", bytes[ctr]);   

      return output.Trim();
   }
}

The following example uses the ByteByByteFormatter class to format integer values. Note that the ICustomFormatter.Format method is called more than once in the second String.Format(IFormatProvider, String, Object[]) method call, and that the default NumberFormatInfo provider is used in the third method call because the .ByteByByteFormatter.Format method does not recognize the "N0" format string and returns a null reference (Nothing in Visual Basic).

Public Module Example
   Public Sub Main()
      Dim value As Long = 3210662321 
      Dim value1 As Byte = 214
      Dim value2 As Byte = 19

      Console.WriteLine((String.Format(New ByteByByteFormatter(), "{0:X}", value)))
      Console.WriteLine((String.Format(New ByteByByteFormatter(), "{0:X} And {1:X} = {2:X} ({2:000})", 
                                      value1, value2, value1 And value2)))                                
      Console.WriteLine(String.Format(New ByteByByteFormatter(), "{0,10:N0}", value))
   End Sub
End Module
' The example displays the following output:
'       00 00 00 00 BF 5E D1 B1
'       00 D6 And 00 13 = 00 12 (018)
'       3,210,662,321
public class Example
{
   public static void Main()
   {
      long value = 3210662321; 
      byte value1 = 214;
      byte value2 = 19;

      Console.WriteLine(String.Format(new ByteByByteFormatter(), "{0:X}", value));
      Console.WriteLine(String.Format(new ByteByByteFormatter(), "{0:X} And {1:X} = {2:X} ({2:000})", 
                                      value1, value2, value1 & value2));                                
      Console.WriteLine(String.Format(new ByteByByteFormatter(), "{0,10:N0}", value));
   }
}
// The example displays the following output:
//       00 00 00 00 BF 5E D1 B1
//       00 D6 And 00 13 = 00 12 (018)
//       3,210,662,321

Back to top

Title

Definition

Standard Numeric Format Strings

Describes standard format strings that create commonly used string representations of numeric values.

Custom Numeric Format Strings

Describes custom format strings that create application-specific formats for numeric values.

Standard Date and Time Format Strings

Describes standard format strings that create commonly used string representations of DateTime values.

Custom Date and Time Format Strings

Describes custom format strings that create application-specific formats for DateTime values.

Standard TimeSpan Format Strings

Describes standard format strings that create commonly used string representations of time intervals.

Custom TimeSpan Format Strings

Describes custom format strings that create application-specific formats for time intervals.

Enumeration Format Strings

Describes standard format strings that are used to create string representations of enumeration values.

Composite Formatting

Describes how to embed one or more formatted values in a string. The string can subsequently be displayed on the console or written to a stream.

Performing Formatting Operations

Lists topics that provide step-by-step instructions for performing specific formatting operations.

Parsing Strings

Describes how to initialize objects to the values described by string representations of those objects. Parsing is the inverse operation of formatting.

Back to top

Reference

System.IFormattable

System.IFormatProvider

System.ICustomFormatter