【发布时间】:2009-11-08 18:38:45
【问题描述】:
IFormattable、IFormatProvider和ICustomFormatter有什么区别和联系,什么时候用?一个简单的实现示例也会非常好。
我并不是说什么时候在 .net 框架中使用它,而是什么时候我会自己实现这些,在这种情况下,哪些类通常会实现什么接口以及如何正确地实现它。
【问题讨论】:
标签: c# formatting
IFormattable、IFormatProvider和ICustomFormatter有什么区别和联系,什么时候用?一个简单的实现示例也会非常好。
我并不是说什么时候在 .net 框架中使用它,而是什么时候我会自己实现这些,在这种情况下,哪些类通常会实现什么接口以及如何正确地实现它。
【问题讨论】:
标签: c# formatting
IFormattable 是一个支持string.Format 格式的对象,即{0:xxx} 中的xxx。如果对象支持接口,string.Format 将委托给对象的 IFormattable.ToString 方法。
IFormatProvider 是格式化程序用于特定文化的日期和货币布局等配置信息的来源。
但是,对于诸如DateTime,您要格式化的实例已经实现 IFormattable 但您无法控制实现(DateTime 在 BCL 中提供,您不能轻易替换它),有一种机制可以防止 @ 987654334@ 来自简单地使用IFormattable.ToString。相反,您实现IFormatProvider,当被要求提供ICustomFormatter 实现时,返回一个。 string.Format 在委托给对象的 IFormattable.Format 之前检查提供程序的 ICustomFormatter,这反过来可能会向 IFormatProvider 询问特定于文化的数据,例如 CultureInfo。
这是一个程序,它显示了string.Format 向IFormatProvider 请求什么,以及控制流程是如何进行的:
using System;
using System.Globalization;
class MyCustomObject : IFormattable
{
public string ToString(string format, IFormatProvider provider)
{
Console.WriteLine("ToString(\"{0}\", provider) called", format);
return "arbitrary value";
}
}
class MyFormatProvider : IFormatProvider
{
public object GetFormat(Type formatType)
{
Console.WriteLine("Asked for {0}", formatType);
return CultureInfo.CurrentCulture.GetFormat(formatType);
}
}
class App
{
static void Main()
{
Console.WriteLine(
string.Format(new MyFormatProvider(), "{0:foobar}",
new MyCustomObject()));
}
}
它打印这个:
Asked for System.ICustomFormatter
ToString("foobar", provider) called
arbitrary value
如果格式提供程序更改为返回自定义格式化程序,它将接管:
class MyFormatProvider : IFormatProvider
{
public object GetFormat(Type formatType)
{
Console.WriteLine("Asked for {0}", formatType);
if (formatType == typeof(ICustomFormatter))
return new MyCustomFormatter();
return CultureInfo.CurrentCulture.GetFormat(formatType);
}
}
class MyCustomFormatter : ICustomFormatter
{
public string Format(string format, object arg, IFormatProvider provider)
{
return string.Format("(format was \"{0}\")", format);
}
}
运行时:
Asked for System.ICustomFormatter
(format was "foobar")
【讨论】:
MyFormatProvider.GetFormat 中返回CultureInfo.CurrentCulture.GetFormat?你会在MyCustomFormatter.Format 中使用IFormatProvider 做什么?
string.Format 的重载之一中指定一个,则使用它。我已经解释了您将使用 IFormatProvider 的用途 - 提供配置信息(例如,询问 CultureInfo)、日期格式(例如,使用 CultureInfo.DateTimeFormat)。
return CultureInfo.CurrentCulture.GetFormat(formatType),它会中断。最终它将被转换为 ICustomFormatter。见:referencesource.microsoft.com/#mscorlib/system/text/…
CultureInfo.CurrentCulture 作为IFormatProvider 它都会调用GetFormat。如果出现问题,则说明CultureInfo 的实例存在问题,因为它实现了IFormatProvider。您是否发现某个特定的实现存在问题?
IFormatProvider(通常为 CultureInfo)支持 GetFormat,如果您想使用当前文化打印小数(例如,分隔符与小数的逗号与点)。如果没有 GetFormat 之前调用 ICustomFormatter,您将无法提供 CultureInfo。
自定义格式基于 3 个组件之间的协调工作:
formattable 对象是可以使用格式提供程序和格式字符串通过实现IFormattable 接口来格式化其数据的实例。基本上,他们会请求格式提供者获取formatter,然后使用格式字符串(格式指令)要求formatter 格式化他们的实例。日期/时间和数字类型是可格式化类型的示例。
格式提供程序是实现IFormatProvider 接口的类。他们负责根据调用者请求的格式类型返回formatter 对象。格式类型可以是格式提供者可以理解的任何类型,而返回的formatter 应该是调用者(在大多数情况下为可格式化的对象)可以用来格式化其数据的任何类型。
格式化程序是负责提供格式化服务的对象。对于日期/时间和数字类型,格式提供程序也是 formatters,即 CultureInfo、DateTimeFormatInfo 和 NumberFormatInfo。
在通过String.Format、Console.WriteLine或StringBuilder.AppendFormat等方法实现的复合格式化中,当格式提供程序传递给它们时,它们总是向格式提供程序询问实现ICustomFormatter的formatter界面。这允许开发人员为这些方法提供各种自定义格式。
【讨论】:
IFormattable 是一个支持不同(命名/自定义)格式的对象 - 例如,数字等。通过使用接口,多个代码块可以使用值和格式字符串,这很常见(对于例如)在数据绑定和string.Format。
IFormatProvider 填补了一些处理格式化的空白——尤其是 i18n。最常见的是,CultureInfo 用作提供者,或者提供特定的本地格式,或者提供不变的文化。
据我所知,ICustomFormatter 是不相关的,并且更多地与序列化(BinaryFormatter)联系在一起。我可能是错的......
IFormattable 对象的示例:
IFormattable d = 123.45M;
string s1 = d.ToString("c", CultureInfo.CurrentCulture), // local currency
s2 = d.ToString("c", CultureInfo.InvariantCulture); // invariant currency
【讨论】:
ICustomFormatter 确实是关于字符串格式化,以及为不是 IFormattable 实现的类型添加对格式字符串的支持(或为IFormattable 对象提供不同的格式字符串)。 IFormatter 是关于序列化的。