如何:定义和使用自定义数值格式提供程序
.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 建议 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
)的其他重载,都会向 IFormatProvider.GetFormat 实现传递表示 NumberFormatInfo 类型的 Type 对象。 此方法应返回 NumberFormatInfo 对象。 如果未返回,将会忽略自定义数字格式提供程序,而改用当前区域性的 NumberFormatInfo 对象。 在此示例中,TelephoneFormatter.GetFormat
方法检查方法参数,并在它表示除 ICustomFormatter 之外的类型时返回 null
,从而处理它可能会被不恰当地传递给数字格式设置方法的情况。
如果自定义数字格式提供程序支持一组格式说明符,请确保提供在 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 对象。