作法:定義和使用自訂數值格式提供者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 參數的數值格式化方法,例如 ToStringYou 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 Recommendation 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),全都會將代表 NumberFormatInfo 類型的 Type 物件傳遞給 IFormatProvider.GetFormat 實作。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 方法會檢查方法參數並傳回 null (如果其代表 ICustomFormatter 以外的類型),藉以處理可能不當傳遞至數值格式化方法的情況。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