Попытка реализовать собственный форматировщик, но ICustomFormatter.Format никогда не вызывается

Я пытаюсь выяснить, как IFormatProvider и ICustomFormatter работают после следующего форматирования TimeSpan в столбце DataGridView о том, как настроить TimeSpan в DataGridView. Я создал полностью собственный форматтер, который всегда возвращает "foo" независимо от того, что он форматирует.

Я использую его на Int, но я предполагаю, что он должен работать на всех типах, так как он не проверяет передаваемое значение, он просто возвращает "foo",

class MyFormatter : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        Console.WriteLine("GetFormat");
        return this;
    }

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        Console.WriteLine("Format");
        return "foo";
    }
}

И я передаю это int.ToString():

int number = 10;
Console.WriteLine(number.ToString(new MyFormatter()));

Что я получаю это:

GetFormat
10

В то время как я надеялся получить это:

GetFormat
Формат
Foo

Изменить: я нашел Как создать и использовать пользовательский IFormatProvider для DateTime? и ответы там говорят, что DateTime.ToString() не примет ничего, кроме DateTimeFormatInfo или же CultureInfo и объект будет отклонен, если он не относится к этим типам, даже если он реализует ICustomFormatter - /questions/17696832/kak-sozdat-i-ispolzovat-polzovatelskij-iformatprovider-dlya-datetime/17696836#17696836.

Поэтому мой вопрос заключается в том, имеет ли это место во всех случаях ToString() методы? Это относится и к DataGridView, и в каких случаях я могу передать действительно специальный форматтер?

2 ответа

Решение

Когда вы вызываете ToString для integer и предоставляете IFormatProvider, он будет пытаться извлечь NumberFormatInfo из него примерно следующим образом:

public static NumberFormatInfo GetInstance(IFormatProvider formatProvider)
{
  CultureInfo cultureInfo = formatProvider as CultureInfo;
  if (cultureInfo != null && !cultureInfo.m_isInherited)
    return cultureInfo.numInfo ?? cultureInfo.NumberFormat;
  NumberFormatInfo numberFormatInfo = formatProvider as NumberFormatInfo;
  if (numberFormatInfo != null)
    return numberFormatInfo;
  if (formatProvider != null)
  {
    NumberFormatInfo format = formatProvider.GetFormat(typeof (NumberFormatInfo)) as NumberFormatInfo;
    if (format != null)
      return format;
  }
  return NumberFormatInfo.CurrentInfo;
}

Итак, вы видите, если все остальное терпит неудачу, это вызывает GetFormat с типом, равным NumberFormatInfoи ожидает NumberFormatInfo назад. Вы не вернете это из GetFormat, так что вместо него используется форматтер по умолчанию (текущая режущая кромка). Так что правильным способом использовать его в этом случае будет что-то вроде:

class MyFormatter : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(NumberFormatInfo)) {
            return new NumberFormatInfo()
            {
                // something here
            };
        }
        Console.WriteLine("GetFormat");
        return this;
    }

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        Console.WriteLine("Format");
        return "foo";
    }
}

Но, таким образом, я сомневаюсь, что вы можете вернуть произвольные значения, такие как "foo" для любого числа.

На самом деле функция ToString() принимает IFormatProvider в качестве аргумента, его подпись гласит:

ToString(IFormatProvider)
ToString(String)
ToString(String,IFormatProvider)

и IFormatProvider имеет только GetFormat(Type) Функция для реализации. ToString() не использует ICustomFormatter который имеет Format() функция. Таким образом, реализация для IFormatProvider является:

class MyFormatter : IFormatProvider
    {
        public object GetFormat(Type formatType)
        {
            Console.WriteLine("GetFormat");
            return this;
        }    
    }
Другие вопросы по тегам