共用方式為


HOW TO:定義和使用自訂數值格式提供者

.NET Framework 可讓您有效掌控數值的字串表示。 它支援以下自訂數值格式的功能:

  • 標準數值格式字串,這些字串提供預先定義的格式集,可將數字轉換成其字串表示。 您可以使用這些字串搭配任何擁有 format 參數的數值格式化方法,例如 Decimal.ToString(String)。 如需詳細資訊,請參閱標準數值格式字串

  • 自訂數值格式字串,這些字串提供能夠合併的符號集,可用來定義自訂數值格式規範。 這些字串可以搭配任何擁有 format 參數的數值格式化方法使用,例如 Decimal.ToString(String)。 如需詳細資訊,請參閱自訂數值格式字串

  • 自訂 CultureInfo 或是 NumberFormatInfo 物件,該物件定義用來顯示數值之字串表示的符號和格式模式。 您可以使用這些模式搭配任何擁有 provider 參數的數值格式化方法,例如 ToString。 通常 provider 參數會用來指定文化特性專屬的格式設定。

在某些情況下,這三項技術並不適用 (例如,應用程式必須顯示格式化帳號、身分證號碼或是郵遞區號時)。 .NET Framework 還可讓您定義決定如何格式化數值的格式化物件,該物件既不是 CultureInfo 也不是 NumberFormatInfo 物件。 本主題將提供實作這類物件的逐步指示,以及提供格式化電話號碼的範例。

定義自訂格式提供者

  1. 定義實作 IFormatProviderICustomFormatter 介面的類別。

  2. 實作 IFormatProvider.GetFormat 方法。 GetFormat 是格式化方法 (例如 String.Format(IFormatProvider, String, Object[]) 方法) 叫用的回呼方法,用來擷取實際負責執行自訂格式顯示的物件。 GetFormat 的一般實作會執行下列操作:

    1. 判斷做為方法參數傳遞的 Type 物件是否代表 ICustomFormatter 介面。

    2. 如果參數確實代表 ICustomFormatter 介面,則 GetFormat 會傳回實作 ICustomFormatter 介面的物件,該介面負責提供自訂的格式顯示。 通常,自訂格式顯示物件會自行傳回。

    3. 如果參數不代表 ICustomFormatter 介面,則 GetFormat 會傳回 null。

  3. 實作 Format 方法。 這個方法是由 String.Format(IFormatProvider, String, Object[]) 方法呼叫,並且負責傳回數字的字串表示。 實作這個方法通常包含下列各項:

    1. (選用) 檢查 provider 參數,確認這個方法的合法目的為提供格式化服務。 針對實作 IFormatProviderICustomFormatter 的格式化物件,這項檢查包含測試 provider 參數是否與目前的格式設定物件相等。

    2. 決定格式化物件是否應支援自訂格式規範 (例如,"N" 格式規範可能表示,美國 電話號碼應使用 NANP 格式輸出,而 "I" 可能表示使用 ITU-T Recommendation E.123 格式輸出)。如果使用格式規範,則這個方法應處理特定格式規範。 格式規範會隨 format 參數傳遞至方法。 如果沒有規範,則 format 參數值為 String.Empty

    3. 擷取做為 arg 參數傳遞至方法的數值。 執行必要的處理方式,將它轉換成其字串表示。

    4. 傳回 arg 參數的字串表示。

使用自訂數值格式化物件

  1. 建立自訂格式類別的新執行個體。

  2. 呼叫 String.Format(IFormatProvider, String, 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, Object[]) 方法使用。 其他擁有 IFormatProvider 型別參數的數值格式化方法多載 (例如 ToString),全都會將代表 NumberFormatInfo 型別的 Type 物件傳遞給 IFormatProvider.GetFormat 實作。 接著這些多載會期待方法傳回 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 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, Object[]) 方法的回呼方法。 因此,它會檢查 formatProvider 參數,以判斷其中是否包含目前 TelephoneFormatter 物件的參考。 不過,這個方法也可以直接從程式碼呼叫。 在這種情況下,您可以使用 formatProvider 參數提供 CultureInfoNumberFormatInfo 物件,該物件會提供文化特性專屬的格式資訊。

編譯程式碼

使用 csc.exe 或 vb.exe 在命令列編輯程式碼。 若要編譯 Visual Studio 中的程式碼,請將程式碼放在主控台應用程式專案範本中。

請參閱

概念

執行格式化作業