Бывают случаи, когда IFormatProvider.GetFormat
вызывается с formatType
, установленным на что-то отличное от типа вашего форматера.
Давайте используем действительно простой тест IFormatProvider
:
public class CustomFormatter : IFormatProvider
{
public object GetFormat(Type formatType)
{
Console.WriteLine("Called with " + formatType);
return null;
}
}
Теперь давайте попробуем кое-что:
string s = 3.ToString(new CustomFormatter());
Здесь GetFormat
вызывается с typeof(NumberFormatInfo)
.
string s = DateTime.Now.ToString(new CustomFormatter());
Здесь, GetFormat
вызывается с typeof(DateTimeFormatInfo)
.
string s = string.Format(new CustomFormatter(), "{0}", 3);
Здесь GetFormat
сначала вызывается с typeof(ICustomFormatter)
, а затем с typeof(NumberFormatInfo)
.
Теперь верно, что в текущая реализация. NET, если вы передадите свой собственный IFormatProvider
в string.Format
, он сначала запросит у него ICustomFormatter
, а если он вернет null
, для NumberFormatInfo
/ DateTimeFormatInfo
(при необходимости).
Однако , вы не можете на это полагаться. Кто-то может использовать ваш IFormatProvider
где-то еще (например, передать объекту, который реализует IFormattable
), и в этом случае ваш IFormatProvider
может быть запрошен для другого formatType
. Может случиться так, что реализация string.Format
изменится в будущем, и он запрашивает NumberFormatInfo
до или , а также и ICustomFormatter
.
The docs для IFormatProvider.GetFormat
скажем:
Возвращает
Экземпляр объекта, указанный formatType
, если IFormatProvider
реализация может предоставить объект такого типа; в противном случае, null
.
Чтобы все работало, сейчас и в будущем, вы должны следовать этому. Если ваш IFormatProvider
может предоставить экземпляр запрошенного типа, он должен это сделать. В противном случае он должен вернуть null
.
Что касается того, почему string.Format
сначала вызывает наш CustomFormatter
с typeof(ICustomFormatter)
, а затем с typeof(NumberFormatInfo)
, см. this do c:
способ форматирования аргументов
Элементы форматирования обрабатываются последовательно с начала строки. Каждый элемент формата имеет индекс, соответствующий объекту в списке аргументов метода. Метод Format извлекает аргумент и получает его строковое представление следующим образом:
Если аргумент равен null
, метод вставляет String.Empty
в строку результата. Вам не нужно беспокоиться об обработке NullReferenceException
для null
аргументов.
Если вы вызываете перегрузку Format(IFormatProvider, String, Object[])
и реализация IFormatProvider.GetFormat
объекта провайдера возвращает ненулевая реализация ICustomFormatter
, аргумент передается методу ICustomFormatter.Format(String, Object, IFormatProvider)
. Если элемент формата содержит аргумент formatString
, он передается в качестве первого аргумента методу. Если реализация ICustomFormatter
доступна и создает ненулевую строку, эта строка возвращается как строковое представление аргумента; в противном случае выполняется следующий шаг.
Если аргумент реализует интерфейс IFormattable
, вызывается его реализация IFormattable.ToString
.
Вызывается метод ToString
без параметров, который либо переопределяет, либо наследует от реализации базового класса.
Аргумент не null
, поэтому мы пропускаем первый пункт .
Мы вызвали перегрузку string.Format
, которая принимает IFormatProvider
, но наша реализация IFormatProvider.GetFormat
вернула null
, поэтому мы пропустили второй пункт.
Наш аргумент, Int32
, выполняет реализацию IFormattable
, поэтому вызывается его реализация IFormattable.ToString
(и передается наша IFormatProvider
). Int32.ToString(IFormatProvider provider)
звонит IFormatProvider.GetFormat
и переходит в typeof(NumberFormatInfo)
.