首页 > 代码库 > 如何告诉类型格式化输出的格式

如何告诉类型格式化输出的格式

分析问题

  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接口一起可以实现灵活强大的格式化输出。