分析问题
IFormatProvider的设计思想是站在类型使用者的角度来提供格式化的方法,这和前文中介绍的IFormattable接口站在类型设计者的角度不同。IFormatProvider只包含了一个方法:object GetFormat(Type formatType)。该方法根据对象的类型给出了一个格式化器,IFormatProvider试图告诉类型用该格式化器去做格式化输出。当然最终的选择权仍然在类型设计者手中,现在在分析一下之前的代码,IFormattable.ToString方法的一开始就对IFormatProvider参数进行判断,当其不为null时,就完全采用使用者提供的格式化方法,这么做是一种信任类型用户的方式,当然,类型的设计者也可以完全不理会IFormatProvider提供的格式化器,这样做确保了类型的安全,但是却提供了非常不友好的接口。
IFormatProvider的GetFormat方法返回一个object类型的格式化器,在通常情况下,一个实现了ICustomFormatter接口的类型对象会被返回,但这一点是无法保证的,以下代码展示了一个常用的实现IFormatProvider的方法,它在之前代码的基础上,提供了IFormatProvider的实现。
using System; namespace Test { class UseIFormatProvider:IFormattable { public DateTime _time; public UseIFormatProvider(DateTime time) { _time = time; } //重写ToString方法 public override string ToString() { return "Object.ToString()"; } public string ToString(string format, IFormatProvider formatProvider) { //这里判断使用者是否提供了格式化器 if (formatProvider!=null) { ICustomFormatter fmt = formatProvider.GetFormat(this.GetType()) as ICustomFormatter; if (fmt!=null) { return fmt.Format(format, this, formatProvider); } } //这里实现格式化输出 switch (format) { case "ld": return _time.ToLongDateString(); case "lt": return _time.ToLongTimeString(); case "lsd": return _time.ToShortDateString(); case "st": return _time.ToShortTimeString(); case "G": case "": case null: default: return _time.ToString(); } } static void Main() { UseIFormatProvider use = new UseIFormatProvider(DateTime.Now); IFormatProvider provider = new MyProvider(); Console.WriteLine(use);//调用的是IFormattable.ToString()方法 //使用这提供格式化方法,格式化字符串不再起作用 Console.WriteLine(use.ToString("lt", provider)); Console.WriteLine(use.ToString("st", provider)); Console.Read(); } } class MyProvider : ICustomFormatter, IFormatProvider { //实现了ICustomFormatter的Format方法 //实际的格式化工作在这里完成 public string Format(string format, object arg, IFormatProvider formatProvider) { UseIFormatProvider obj = arg as UseIFormatProvider; if (obj==null) { return arg.ToString(); } return obj._time.ToString("yyyy-MM-dd hh:mm:ss"); } //本类型可以实现对UseIFormatProvider类型的格式化 public object GetFormat(Type formatType) { if (formatType==typeof(UseIFormatProvider)) { return this; } else { return null; } } } }
如以上代码所示,MyProvider的GetFormat方法返回了一个实现了ICustomFormat接口的类型对象,这在以上代码中就是MyProvider的对象本身,并且在Format方法中实现了具体的格式化方法。
在通常情况下,由类型使用者来实现格式化并不容易,因为他不能随心所欲地访问类型的内部成员。而类型的设计者实现格式化输出时,又势必很难覆盖所有类型使用这的需求,这就需要两者进行有效的协调。就如以上代码所展示的那样,类型设计者实现了一部分常用的格式化需求,并且允许使用者提供他们自己的格式化方法。
答案
IFormatProvider让类型的使用者有机会提供格式化的方法。GetFormat方法返回一个格式化器,在通常情况下,该格式化器的类型是一个实现了ICustomFormatter的类型对象。IFormatProvider接口和IFormattable接口一起可以实现灵活强大的格式化输出。
如何告诉类型格式化输出的格式