如何:定义和使用自定义数值格式提供程序How to: Define and Use Custom Numeric Format Providers

.NET Framework 使你可以全面控制数值的字符串表示形式。The .NET Framework gives you extensive control over the string representation of numeric values. 它支持用于自定义数值格式的以下功能:It supports the following features for customizing the format of numeric values:

  • 标准数字格式字符串,提供一组预定义格式以用于将数字转换为其字符串表示形式。Standard numeric format strings, which provide a predefined set of formats for converting numbers to their string representation. 可以将它们与包含 format 参数的任何数字格式设置方法(如 Decimal.ToString(String))结合使用。You can use them with any numeric formatting method, such as Decimal.ToString(String), that has a format parameter. 有关详细信息,请参阅标准数字格式字符串For details, see Standard Numeric Format Strings.

  • 自定义数字格式字符串,提供一组可以进行组合以定义自定义数字格式说明符的符号。Custom numeric format strings, which provide a set of symbols that can be combined to define custom numeric format specifiers. 它们还可以与包含 format 参数的任何数字格式设置方法(如 Decimal.ToString(String))结合使用。They can also be used with any numeric formatting method, such as Decimal.ToString(String), that has a format parameter. 有关详细信息,请参阅自定义数字格式字符串For details, see Custom Numeric Format Strings.

  • 自定义 CultureInfoNumberFormatInfo 对象,定义用于显示数值的字符串表示形式的符号和格式模式。Custom CultureInfo or NumberFormatInfo objects, which define the symbols and format patterns used in displaying the string representations of numeric values. 可以将它们与包含 provider 参数的任何数字格式设置方法(如 ToString)结合使用。You can use them with any numeric formatting method, such as ToString, that has a provider parameter. provider 参数通常用于指定区域性专用格式设置。Typically, the provider parameter is used to specify culture-specific formatting.

在某些情况下(例如当应用程序必须显示格式化帐号、标识号或邮政编码),这三种方法都不合适。In some cases (such as when an application must display a formatted account number, an identification number, or a postal code) these three techniques are inappropriate. 借助 .NET Framework,还可以定义既不是 CultureInfo 也不是 NumberFormatInfo 对象的格式设置对象,用于确定如何设置数值的格式。The .NET Framework also enables you to define a formatting object that is neither a CultureInfo nor a NumberFormatInfo object to determine how a numeric value is formatted. 本主题提供用于实现这类对象的分步说明,并提供对电话号码设置格式的示例。This topic provides the step-by-step instructions for implementing such an object, and provides an example that formats telephone numbers.

定义自定义格式提供程序To define a custom format provider

  1. 定义实现 IFormatProviderICustomFormatter 接口的类。Define a class that implements the IFormatProvider and ICustomFormatter interfaces.

  2. 实现 IFormatProvider.GetFormat 方法。Implement the IFormatProvider.GetFormat method. GetFormat 是格式设置方法(如 String.Format(IFormatProvider, String, Object[]) 方法)调用的回调方法,用于检索实际负责执行自定义格式设置的对象。GetFormat is a callback method that the formatting method (such as the String.Format(IFormatProvider, String, Object[]) method) invokes to retrieve the object that is actually responsible for performing custom formatting. GetFormat 的典型实现执行以下操作:A typical implementation of GetFormat does the following:

    1. 确定以方法参数形式传递的 Type 对象是否表示 ICustomFormatter 接口。Determines whether the Type object passed as a method parameter represents an ICustomFormatter interface.

    2. 如果此参数确实表示 ICustomFormatter 接口,GetFormat 会返回对象,用于实现负责执行自定义格式设置的 ICustomFormatter 接口。If the parameter does represent the ICustomFormatter interface, GetFormat returns an object that implements the ICustomFormatter interface that is responsible for providing custom formatting. 通常,自定义格式设置对象返回其自身。Typically, the custom formatting object returns itself.

    3. 如果参数不表示 ICustomFormatter 接口,GetFormat 返回的是 nullIf the parameter does not represent the ICustomFormatter interface, GetFormat returns null.

  3. 实现 Format 方法。Implement the Format method. 此方法由 String.Format(IFormatProvider, String, Object[]) 方法调用,负责返回数字的字符串表示形式。This method is called by the String.Format(IFormatProvider, String, Object[]) method and is responsible for returning the string representation of a number. 实现方法通常涉及以下步骤:Implementing the method typically involves the following:

    1. (可选)通过检查 provider 参数,确保此方法旨在以合法方式提供格式设置服务。Optionally, make sure that the method is legitimately intended to provide formatting services by examining the provider parameter. 对于实现 IFormatProviderICustomFormatter 的格式设置对象,这涉及测试 provider 参数是否与当前格式设置对象相等。For formatting objects that implement both IFormatProvider and ICustomFormatter, this involves testing the provider parameter for equality with the current formatting object.

    2. 确定格式设置对象是否应支持自定义格式说明符。Determine whether the formatting object should support custom format specifiers. (例如,格式说明符“N”可能指示应以 NANP 格式输出美国电话号码,而“I”可能指示以 ITU-T 建议 E.123 格式进行输出。)如果使用格式说明符,则方法应处理特定格式说明符。(For example, an "N" format specifier might indicate that a U.S. telephone number should be output in NANP format, and an "I" might indicate output in ITU-T Recommendation E.123 format.) If format specifiers are used, the method should handle the specific format specifier. 它会在 format 参数中传递给方法。It is passed to the method in the format parameter. 如果没有说明符,format 参数的值是 String.EmptyIf no specifier is present, the value of the format parameter is String.Empty.

    3. 检索作为 arg 参数传递给方法的数值。Retrieve the numeric value passed to the method as the arg parameter. 执行将它转换为其字符串表示形式所需的任何操作。Perform whatever manipulations are required to convert it to its string representation.

    4. 返回 arg 参数的字符串表示形式。Return the string representation of the arg parameter.

使用自定义数字格式设置对象To use a custom numeric formatting object

  1. 创建自定义格式设置类的新实例。Create a new instance of the custom formatting class.

  2. 调用 String.Format(IFormatProvider, String, Object[]) 格式设置方法,同时向它传递自定义格式设置对象、格式设置说明符(或 String.Empty,如果未使用说明符的话),以及要设置格式的数值。Call the String.Format(IFormatProvider, String, Object[]) formatting method, passing it the custom formatting object, the formatting specifier (or String.Empty, if one is not used), and the numeric value to be formatted.

示例Example

下面的示例定义了一个名为 TelephoneFormatter 的自定义数值格式提供程序,该提供程序将代表美国电话号码的数字转化为它的 NANP 或 E.123 格式。The following example defines a custom numeric format provider named TelephoneFormatter that converts a number that represents a U.S. telephone number to its NANP or E.123 format. 该方法处理两个格式说明符“N”(输出 NANP 格式)和“I”(输出国际 E.123 格式)。The method handles two format specifiers, "N" (which outputs the NANP format) and "I" (which outputs the international E.123 format).

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[]) 方法配合使用。The custom numeric format provider can be used only with the String.Format(IFormatProvider, String, Object[]) method. 包含 IFormatProvider 类型参数的数字格式设置方法(如 ToString)的其他重载,都会向 IFormatProvider.GetFormat 实现传递表示 NumberFormatInfo 类型的 Type 对象。The other overloads of numeric formatting methods (such as ToString) that have a parameter of type IFormatProvider all pass the IFormatProvider.GetFormat implementation a Type object that represents the NumberFormatInfo type. 此方法应返回 NumberFormatInfo 对象。In return, they expect the method to return a NumberFormatInfo object. 如果未返回,将会忽略自定义数字格式提供程序,而改用当前区域性的 NumberFormatInfo 对象。If it does not, the custom numeric format provider is ignored, and the NumberFormatInfo object for the current culture is used in its place. 在此示例中,TelephoneFormatter.GetFormat 方法检查方法参数,并在它表示除 ICustomFormatter 之外的类型时返回 null,从而处理它可能会被不恰当地传递给数字格式设置方法的情况。In the example, the TelephoneFormatter.GetFormat method handles the possibility that it may be inappropriately passed to a numeric formatting method by examining the method parameter and returning null if it represents a type other than ICustomFormatter.

如果自定义数字格式提供程序支持一组格式说明符,请确保提供在 String.Format(IFormatProvider, String, Object[]) 方法调用中使用的格式项没有格式说明符时的默认行为。If a custom numeric format provider supports a set of format specifiers, make sure you provide a default behavior if no format specifier is supplied in the format item used in the String.Format(IFormatProvider, String, Object[]) method call. 在示例中,“N”是默认格式说明符。In the example, "N" is the default format specifier. 这使数字可以通过提供显式格式说明符来转换为格式化电话号码。This allows for a number to be converted to a formatted telephone number by providing an explicit format specifier. 下面的示例演示了此类方法调用。The following example illustrates such a method call.

Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}", 4257884748));
Console.WriteLine(String.Format(New TelephoneFormatter, "{0:N}", 4257884748))

但是它还允许在不存在格式说明符时进行转换。But it also allows the conversion to occur if no format specifier is present. 下面的示例演示了此类方法调用。The following example illustrates such a method call.

Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 4257884748));
Console.WriteLine(String.Format(New TelephoneFormatter, "{0}", 4257884748))

如果未定义默认格式说明符,ICustomFormatter.Format 方法实现应包含如下代码,以便 .NET 能够提供代码不支持的格式设置。If no default format specifier is defined, your implementation of the ICustomFormatter.Format method should include code such as the following so that .NET can provide formatting that your code does not support.

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[]) 方法的回调方法。In the case of this example, the method that implements ICustomFormatter.Format is intended to serve as a callback method for the String.Format(IFormatProvider, String, Object[]) method. 因此,它会检查 formatProvider 参数,以确定它是否包含对当前 TelephoneFormatter 对象的引用。Therefore, it examines the formatProvider parameter to determine whether it contains a reference to the current TelephoneFormatter object. 但是,也可以直接从代码调用该方法。However, the method can also be called directly from code. 在这种情况下,可以使用 formatProvider 参数,提供用于提供区域性专用格式设置信息的 CultureInfoNumberFormatInfo 对象。In that case, you can use the formatProvider parameter to provide a CultureInfo or NumberFormatInfo object that supplies culture-specific formatting information.

请参阅See also