Практическое руководство. Определение и использование настраиваемых поставщиков числовых форматов
Обновлен: Ноябрь 2007
.NET Framework предоставляет расширенный контроль над строковым представлением числовых значений. Он поддерживает следующие возможности для настройки форматов числовых значений:
Строки стандартных числовых форматов, которые предоставляют стандартный набор форматов для преобразования чисел в их строковое представление. Их можно использовать с любым методом числового форматирования, например Decimal.ToString(String) с параметром format. Дополнительные сведения см. в разделе Строки стандартных числовых форматов.
Строки пользовательских числовых форматов, предоставляющих набор символов, которые могут быть объединены для определения спецификаторов пользовательского числового формата. Они также могут быть использованы с любым методом числового форматирования, например Decimal.ToString(String) с параметром format. Дополнительные сведения см. в разделе Строки настраиваемых числовых форматов.
Настраиваемые объекты CultureInfo или NumberFormatInfo, которые определяют символы и шаблоны форматирования, используемые при отображении строковых представлений числовых значений. Их можно использовать с любым методом числового форматирования, например ToString с параметром provider. Как правило, параметр provider используется для указания форматирования, зависящего от региональных параметров.
В некоторых случаях (например, когда приложению необходимо отобразить отформатированный номер учетной записи, идентификационный номер или почтовый индекс) эти три метода неприменимы. .NET Framework также позволяет определить объект форматирования, который не является ни CultureInfo, ни объектом NumberFormatInfo, для определения порядка форматирования числовых значений. Этот раздел содержит подробные инструкции по реализации таких объектов и пример форматирования телефонных номеров.
Определение поставщика пользовательского формата
Определите класс, который реализует интерфейсы IFormatProvider и ICustomFormatter.
Реализуйте метод IFormatProvider.GetFormat. GetFormat представляет собой метод обратного вызова, который вызывается методом форматирования (таким как метод String.Format(IFormatProvider, String, array<Object[])) для получения объекта, фактически отвечающего за выполнение пользовательского форматирования. Обычно реализация метода GetFormat выполняет следующие действия.
Определяет, представляет ли объект Type, передаваемый в качестве параметра метода, интерфейс ICustomFormatter.
Если параметр представляет собой интерфейс ICustomFormatter, то метод GetFormat возвращает объект, реализующий интерфейс ICustomFormatter, который отвечает за предоставление пользовательского форматирования. Как правило, объект пользовательского форматирования возвращает сам себя.
Если параметр не представляет собой интерфейс ICustomFormatter, то метод GetFormat возвращает null.
Реализуйте метод Format. Этот метод вызывается методом String.Format(IFormatProvider, String, array<Object[]) и возвращает строковое представление числа. Реализация этого метода обычно включает в себя следующее:
При необходимости убедитесь, что метод предназначен для предоставления служб форматирования, проверив параметр provider. Для объектов форматирования, реализующих IFormatProvider и ICustomFormatter, это включает в себя проверку параметра provider на равенство с текущим объектом форматирования.
Определите, должен ли объект форматирования поддерживать спецификаторы пользовательского формата. (Например, спецификатор формата "N" может означать, что телефонный номер США следует выводить в формате NANP, а "I" может означать вывод по рекомендации ITU-T формата E.123.) Если используются спецификаторы формата, то метод должен обрабатывать этот спецификатор определенного формата. Он передается методу в качестве параметра format. Если спецификатор отсутствует, то значением параметра format является String.Empty.
Получите числовое значение, передаваемое в метод в качестве параметра arg. Выполните операции, необходимые для его преобразования в строковое представление.
Верните строковое представление параметра arg.
Использование объекта пользовательского числового форматирования
Создайте новый экземпляр класса пользовательского форматирования.
Вызовите метод форматирования String.Format(IFormatProvider, String, array<Object[]), передавая ему объект пользовательского форматирования, спецификатор форматирования (или String.Empty, если он не используется) и числовое значение, подлежащее форматированию.
Пример
В следующем примере определяется поставщик пользовательского числового формата с именем TelephoneFormatter, который преобразует число, представляющее номер телефона в США, в формат NANP или E.123. Метод обрабатывает два спецификатора формата, "N" (вывод в формате NANP) и "I" (вывод в международном формате E.123).
Public Class TelephoneFormatter : Implements IFormatProvider, ICustomFormatter
Public Function GetFormat(formatType As Type) As Object _
Implements IFormatProvider.GetFormat
If formatType Is GetType(ICustomFormatter) Then
Return Me
Else
Return Nothing
End If
End Function
Public Function Format(fmt As String, arg As Object, _
formatProvider As IFormatProvider) As String _
Implements ICustomFormatter.Format
' Check whether this is an appropriate callback
If Not Me.Equals(formatProvider) Then Return Nothing
' Set default format specifier
If String.IsNullOrEmpty(fmt) Then fmt = "N"
Dim numericString As String = arg.ToString
If fmt = "N" Then
Select Case numericString.Length
Case <= 4
Return numericString
Case 7
Return Left(numericString, 3) & "-" & Mid(numericString, 4)
Case 10
Return "(" & Left(numericString, 3) & ") " & _
Mid(numericString, 4, 3) & "-" & Mid(numericString, 7)
Case Else
Throw New FormatException( _
String.Format("'{0}' cannot be used to format {1}.", _
fmt, arg.ToString()))
End Select
ElseIf fmt = "I" Then
If numericString.Length < 10 Then
Throw New FormatException(String.Format("{0} does not have 10 digits.", arg.ToString()))
Else
numericString = "+1 " & Left(numericString, 3) & " " & Mid(numericString, 4, 3) & " " & Mid(numericString, 7)
End If
Else
Throw New FormatException(String.Format("The {0} format specifier is invalid.", fmt))
End If
Return numericString
End Function
End Class
Public Module TestTelephoneFormatter
Public Sub Main
Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 0))
Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 911))
Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 8490216))
Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 4257884748))
Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 0))
Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 911))
Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 8490216))
Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 4257884748))
Console.WriteLine(String.Format(New TelephoneFormatter, "{0:I}", 4257884748))
End Sub
End Module
using System;
using System.Globalization;
public class TelephoneFormatter : IFormatProvider, ICustomFormatter
{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}
public string Format(string format, object arg, IFormatProvider formatProvider)
{
// Check whether this is an appropriate callback
if (! this.Equals(formatProvider))
return null;
// Set default format specifier
if (string.IsNullOrEmpty(format))
format = "N";
string numericString = arg.ToString();
if (format == "N")
{
if (numericString.Length <= 4)
return numericString;
else if (numericString.Length == 7)
return numericString.Substring(0, 3) + "-" + numericString.Substring(3, 4);
else if (numericString.Length == 10)
return "(" + numericString.Substring(0, 3) + ") " +
numericString.Substring(3, 3) + "-" + numericString.Substring(6);
else
throw new FormatException(
string.Format("'{0}' cannot be used to format {1}.",
format, arg.ToString()));
}
else if (format == "I")
{
if (numericString.Length < 10)
throw new FormatException(string.Format("{0} does not have 10 digits.", arg.ToString()));
else
numericString = "+1 " + numericString.Substring(0, 3) + " " + numericString.Substring(3, 3) + " " + numericString.Substring(6);
}
else
{
throw new FormatException(string.Format("The {0} format specifier is invalid.", format));
}
return numericString;
}
}
public class TestTelephoneFormatter
{
public static void Main()
{
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 0));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 911));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 8490216));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 4257884748));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 0));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 911));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 8490216));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 4257884748));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:I}", 4257884748));
}
}
Поставщик пользовательского числового формата может использоваться только с методом String.Format(IFormatProvider, String, array<Object[]). Другие перегрузки методов числового форматирования (например ToString) с параметром типа IFormatProvider передают реализацию метода IFormatProvider.GetFormat для объекта Type, представляющего тип NumberFormatInfo. В свою очередь, они ожидают, что метод возвратит объект NumberFormatInfo. Если это не так, то поставщик пользовательского числового формата игнорируется, и вместо него используется объект NumberFormatInfo, соответствующий текущим региональным параметрам. В этом примере метод TelephoneFormatter.GetFormat обрабатывает вероятность некорректной передачи методу числового форматирования, проверяя параметр метода и возвращая null, если он представляет тип, отличный от ICustomFormatter.
Если поставщик пользовательского числового формата поддерживает набор спецификаторов формата, убедитесь, что также предоставлено поведение по умолчанию, если спецификатор формата не предоставляется в элементе форматирования, используемом при вызове метода String.Format(IFormatProvider, String, array<Object[]). В приведенном примере "N" — это спецификатор формата по умолчанию. Это позволяет преобразовать число в формат телефонного номера, явно предоставляя спецификатор формата. В следующем примере показан такой вызов метода.
Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 4257884748))
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 4257884748));
Но это также позволяет произвести преобразование в случае, если спецификатор формата отсутствует. В следующем примере показан такой вызов метода.
Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 4257884748))
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 4257884748));
Если не определен спецификатор формата по умолчанию, то реализация метода ICustomFormatter.Format должна содержать код, аналогичный приведенному ниже, чтобы в .NET Framework могло быть представлено форматирование, которое не поддерживается кодом.
If TypeOf(arg) Is IFormattable Then
s = DirectCast(arg, IFormattable).ToString(fmt, formatProvider)
ElseIf arg IsNot Nothing Then
s = arg.ToString()
End If
if (arg is IFormattable)
s = ((IFormattable)arg).ToString(format, formatProvider);
else if (arg != null)
s = arg.ToString();
В таком случае в этом примере метод, реализующий ICustomFormatter.Format, служит методом обратного вызова для метода String.Format(IFormatProvider, String, array<Object[]). Таким образом, он проверяет параметр formatProvider на наличие ссылки на текущий объект TelephoneFormatter. Тем не менее, метод можно также вызвать непосредственно из кода. В этом случае можно использовать параметр formatProvider для предоставления CultureInfo или объекта NumberFormatInfo, предоставляющего сведения о форматировании для соответствующих региональных параметров.
Компиляция кода
Откомпилируйте код из командной строки, используя csc.exe или vb.exe. Чтобы откомпилировать код в Visual Studio, поместите его в шаблон проекта консольного приложения.