C#: Connection between IFormattable, IFormatProvider and ICustomFormatter, and when to use what
c#, formatting
Solution
`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")
Problem
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.