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")