What are the difference and connection between IFormattable
, IFormatProvider
and ICustomFormatter
and when would they be used? A simple implementation example would be very nice too.
And I don't really mean when it is used in the .net framework, but when I would implement these myself and in that case what classes would typically implement what interface and how to do it properly.
IFormattable
is an object which supports formats in string.Format
, i.e. the xxx
in {0:xxx}
. string.Format
will delegate to an object's IFormattable.ToString
method if the object supports the interface.
IFormatProvider
is a source of configuration information that formatters use for things like culture-specific date and currency layout.
However, for situations like e.g. DateTime
, where the instance you want to format already implements IFormattable
yet you don't control the implementation (DateTime
is supplied in the BCL, you can't replace it easily), there is a mechanism to prevent string.Format
from simply using IFormattable.ToString
. Instead, you implement IFormatProvider
, and when asked for an ICustomFormatter
implementation, return one. string.Format
checks the provider for an ICustomFormatter
before it delegates to the object's IFormattable.Format
, which would in turn likely ask the IFormatProvider
for culture-specific data like CultureInfo
.
Here is a program which shows what string.Format
asks the IFormatProvider
for, and how the flow of control goes:
using System;
using System.Globalization;
class MyCustomObject : IFormattable
{
public string ToString(string format, IFormatProvider provider)
{
Console.WriteLine("ToString(\"{0}\", provider) called", format);
return "arbitrary value";
}
}
class MyFormatProvider : IFormatProvider
{
public object GetFormat(Type formatType)
{
Console.WriteLine("Asked for {0}", formatType);
return CultureInfo.CurrentCulture.GetFormat(formatType);
}
}
class App
{
static void Main()
{
Console.WriteLine(
string.Format(new MyFormatProvider(), "{0:foobar}",
new MyCustomObject()));
}
}
It prints this:
Asked for System.ICustomFormatter
ToString("foobar", provider) called
arbitrary value
If the format provider is changed to return a custom formatter, it takes over:
class MyFormatProvider : IFormatProvider
{
public object GetFormat(Type formatType)
{
Console.WriteLine("Asked for {0}", formatType);
if (formatType == typeof(ICustomFormatter))
return new MyCustomFormatter();
return CultureInfo.CurrentCulture.GetFormat(formatType);
}
}
class MyCustomFormatter : ICustomFormatter
{
public string Format(string format, object arg, IFormatProvider provider)
{
return string.Format("(format was \"{0}\")", format);
}
}
When run:
Asked for System.ICustomFormatter
(format was "foobar")
IFormattable
is an object that supports different (named/custom) formats - for example, numbers, etc. By using an interface, multiple blocks of code can use the value and a format string, and this is common (for example) in data-binding and string.Format
.
An IFormatProvider
fills in some gaps dealing with formatting - particularly i18n. Most commonly, a CultureInfo
is used as the provider, either giving a specific local format, or the invariant culture.
As far as I know, ICustomFormatter
is unrelated, and ties more into serialization (BinaryFormatter
). I could be wrong...
An example of an IFormattable
object:
IFormattable d = 123.45M;
string s1 = d.ToString("c", CultureInfo.CurrentCulture), // local currency
s2 = d.ToString("c", CultureInfo.InvariantCulture); // invariant currency