【问题标题】:C#: Connection between IFormattable, IFormatProvider and ICustomFormatter, and when to use whatC#:IFormattable、IFormatProvider 和 ICustomFormatter 之间的连接,以及何时使用什么
【发布时间】:2009-11-08 18:38:45
【问题描述】:

IFormattableIFormatProviderICustomFormatter有什么区别和联系,什么时候用?一个简单的实现示例也会非常好。

我并不是说什么时候在 .net 框架中使用它,而是什么时候我会自己实现这些,在这种情况下,哪些类通常会实现什么接口以及如何正确地实现它。

【问题讨论】:

    标签: c# formatting


    【解决方案1】:
    • 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.FormatIFormatProvider 请求什么,以及控制流程是如何进行的:

    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 做什么?
    • 默认格式提供者是 CultureInfo.CurrentCulture;如果您没有在string.Format 的重载之一中指定一个,则使用它。我已经解释了您将使用 IFormatProvider 的用途 - 提供配置信息(例如,询问 CultureInfo)、日期格式(例如,使用 CultureInfo.DateTimeFormat)。
    • 不要在 GetFormat 中使用return CultureInfo.CurrentCulture.GetFormat(formatType),它会中断。最终它将被转换为 ICustomFormatter。见:referencesource.microsoft.com/#mscorlib/system/text/…
    • @Wouter 每次你使用CultureInfo.CurrentCulture 作为IFormatProvider 它都会调用GetFormat。如果出现问题,则说明CultureInfo 的实例存在问题,因为它实现了IFormatProvider。您是否发现某个特定的实现存在问题?
    • 查看:referencesource.microsoft.com/#mscorlib/system/…referencesource.microsoft.com/#mscorlib/system/globalization/…,了解代码路径如何期望 IFormatProvider(通常为 CultureInfo)支持 GetFormat,如果您想使用当前文化打印小数(例如,分隔符与小数的逗号与点)。如果没有 GetFormat 之前调用 ICustomFormatter,您将无法提供 CultureInfo
    【解决方案2】:

    自定义格式基于 3 个组件之间的协调工作:

    • 可格式化
    • 格式提供程序
    • 格式化程序

    formattable 对象是可以使用格式提供程序和格式字符串通过实现IFormattable 接口来格式化其数据的实例。基本上,他们会请求格式提供者获取formatter,然后使用格式字符串(格式指令)要求formatter 格式化他们的实例。日期/时间和数字类型是可格式化类型的示例。

    格式提供程序是实现IFormatProvider 接口的类。他们负责根据调用者请求的格式类型返回formatter 对象。格式类型可以是格式提供者可以理解的任何类型,而返回的formatter 应该是调用者(在大多数情况下为可格式化的对象)可以用来格式化其数据的任何类型。

    格式化程序是负责提供格式化服务的对象。对于日期/时间和数字类型,格式提供程序也是 formatters,即 CultureInfoDateTimeFormatInfoNumberFormatInfo

    在通过String.FormatConsole.WriteLineStringBuilder.AppendFormat等方法实现的复合格式化中,当格式提供程序传递给它们时,它们总是向格式提供程序询问实现ICustomFormatterformatter界面。这允许开发人员为这些方法提供各种自定义格式。

    【讨论】:

      【解决方案3】:

      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 是关于序列化的。
      • IFormattable 也用于字符串插值。
      猜你喜欢
      • 1970-01-01
      • 2020-04-29
      • 2018-07-04
      • 2013-02-15
      • 1970-01-01
      • 1970-01-01
      • 2014-07-10
      • 2015-10-31
      • 1970-01-01
      相关资源
      最近更新 更多