如何:定義和使用自訂數值格式提供者
.NET 可讓您有效掌控數值的字串表示。 它支援以下自訂數值格式的功能:
標準數值格式字串,這些字串提供預先定義的格式集,可將數字轉換成其字串表示。 您可以使用它們搭配任何擁有
format
參數的數值格式化方法,例如 Decimal.ToString(String)。 如需詳細資訊,請參閱標準數值格式字串。自訂數值格式字串,這些字串提供能夠合併的符號集,可用來定義自訂數值格式規範。 它們也可以搭配任何擁有
format
參數的數值格式化方法使用,例如 Decimal.ToString(String)。 如需詳細資訊,請參閱自訂數值格式字串。自訂的 CultureInfo 或 NumberFormatInfo 物件,這類物件會定義用來顯示數值字串表示的符號和格式模式。 您可以使用它們搭配任何擁有
provider
參數的數值格式化方法,例如 ToString。 一般而言,會使用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 Recommendation E.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[]) 方法搭配使用。 其他擁有 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 才能提供您程式碼不支援的格式。
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 物件,該物件會提供文化特性特定的格式資訊。