Практическое руководство. Определение и использование настраиваемых поставщиков числовых форматов
.NET обеспечивает расширенный контроль над строковым представлением числовых значений. Эта платформа поддерживает указанные далее возможности для настройки форматов числовых значений.
Строки стандартных числовых форматов, которые предоставляют стандартный набор форматов для преобразования чисел в их строковое представление. Вы можете использовать их с любым методом числового форматирования, например Decimal.ToString(String) с параметром
format
. Дополнительные сведения см. в статье Строки стандартных числовых форматов.Строки настраиваемых числовых форматов, предоставляющих набор символов, которые могут быть объединены для определения описателей настраиваемого числового формата. Их можно использовать с любым методом числового форматирования, например Decimal.ToString(String) с параметром
format
. Дополнительные сведения см. в разделе Строки настраиваемых числовых форматов.Настраиваемые объекты CultureInfo и NumberFormatInfo, которые определяют символы и шаблоны форматирования для отображения строковых представлений числовых значений. Вы можете использовать их с любым методом числового форматирования, например ToString с параметром
provider
. Как правило, параметрprovider
используется для указания форматирования, зависящего от языка и региональных параметров.
В некоторых случаях (например, когда приложению необходимо отобразить отформатированный номер учетной записи, идентификационный номер или почтовый индекс) эти три метода неприменимы. Кроме того, .NET позволяет определить объект форматирования, который не является объектом CultureInfo или NumberFormatInfo, для определения порядка форматирования числовых значений. Этот раздел содержит пошаговые инструкции по реализации таких объектов и пример форматирования телефонных номеров.
Определение поставщика пользовательского формата
Определите класс, реализующий интерфейсы IFormatProvider и ICustomFormatter.
Реализуйте метод IFormatProvider.GetFormat. GetFormat — это метод обратного вызова, с помощью которого метод форматирования (например, String.Format(IFormatProvider, String, Object[])) вызывает объект, отвечающий за выполнение пользовательского форматирования. Метод GetFormat в типичной реализации выполняет следующие действия:
Определяет, предоставляет ли объект Type, полученный в качестве параметра, интерфейс ICustomFormatter.
Если параметр представляет интерфейс ICustomFormatter, то метод GetFormat возвращает объект, реализующий интерфейс ICustomFormatter, который отвечает за применение пользовательского форматирования. Как правило, объект пользовательского форматирования возвращает сам себя.
Если параметр не представляет интерфейс ICustomFormatter, GetFormat возвращает
null
.
Реализуйте метод Format. Этот метод вызывается из метода String.Format(IFormatProvider, String, Object[]) и возвращает строковое представление числа. Реализация этого метода обычно включает в себя выполнение следующих действий.
Вы можете проверить параметр
provider
, чтобы убедиться, что метод действительно предназначен для форматирования. Для объектов форматирования, которые реализуют интерфейсы IFormatProvider и ICustomFormatter, нужно проверить еще и параметрprovider
, значение которого должно совпадать с текущим объектом форматирования.Определите, должен ли объект форматирования поддерживать описатели настраиваемого формата. (Например, описатель формата N может указывать, что номер телефона США должен быть выходным в формате NANP, а "I" может указывать выходные данные в формате ITU-T 123.) Если используются описатели формата, метод должен обрабатывать конкретный описатель формата. Он передается методу в параметре
format
. Если описатель отсутствует, значением параметраformat
является String.Empty.Получите числовое значение, передаваемое методу в параметре
arg
. Выполните операции, необходимые для его преобразования в строковое представление.Верните строковое представление параметра
arg
.
Использование объекта настраиваемого числового форматирования
Создайте новый экземпляр класса настраиваемого форматирования.
Вызовите метод форматирования String.Format(IFormatProvider, String, Object[]), передав ему объект пользовательского форматирования, описатель форматирования (или String.Empty, если описатель не используется) и числовое значение для форматирования.
Пример
В следующем примере определяется поставщик настраиваемого числового формата с именем TelephoneFormatter
, который преобразует число, представляющее номер телефона в США, в формат NANP или E.123. Метод обрабатывает два описателя формата "N" (вывод в формате NANP) и "I" (вывод в международном формате E.123).
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));
}
}
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
Поставщик настраиваемого числового формата можно использовать только с методом String.Format(IFormatProvider, String, Object[]). Другие перегрузки методов числового форматирования (например, ToString
), у которых параметр имеет тип IFormatProvider, передают в реализацию метода IFormatProvider.GetFormat объект Type, представляющий тип NumberFormatInfo. В ответ они ожидают получить объект NumberFormatInfo. Если это не так, поставщик настраиваемого числового формата игнорируется, и вместо него используется объект NumberFormatInfo для текущих языка и региональных параметров. В нашем примере метод TelephoneFormatter.GetFormat
обрабатывает случай некорректной передачи его в метод числового форматирования. Для этого он проверяет параметр метода и возвращает null
, если его тип отличается от ICustomFormatter.
Если поставщик настраиваемого числового формата поддерживает набор описателей формата, обязательно предоставьте поведение по умолчанию на случай, если описатель формата не указан в элементе форматирования при вызове метода String.Format(IFormatProvider, String, 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 предоставила не поддерживаемое в коде форматирование.
if (arg is IFormattable)
s = ((IFormattable)arg).ToString(format, formatProvider);
else if (arg != null)
s = arg.ToString();
If TypeOf (arg) Is IFormattable Then
s = DirectCast(arg, IFormattable).ToString(fmt, formatProvider)
ElseIf arg IsNot Nothing Then
s = arg.ToString()
End If
В нашем примере метод, который реализует ICustomFormatter.Format, будет использоваться как метод обратного вызова для метода String.Format(IFormatProvider, String, Object[]). Это значит, что он должен проверить параметр formatProvider
на наличие ссылки на текущий объект TelephoneFormatter
. Тем не менее, метод можно также вызвать непосредственно из кода. В этом случае вы можете использовать параметр formatProvider
для предоставления объекта CultureInfo или NumberFormatInfo со сведениями о форматировании для выбранного языка и региональных параметров.