ICustomFormatter及IFormatProvider接口用法揭秘

本文通过实例演示了如何使用 .NET 中的 IFormatProvider 和 ICustomFormatter 接口来自定义格式化逻辑。通过创建 MyFormater 类实现了对整型变量的不同格式化输出。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在学习IFormatProvider接口的用法时,在网络上找了段实例代码(具体哪个网站不记得了,就不给出链接了),通过研究实例代码,初步了解了IFormatProvider接口的用法。

在学习代码之前,我们先来了解一下本例中使用到的两个接口ICustomFormatterIFormatProvider

查看MSDN,得到以下关于ICustomFormatterIFormatProvider接口的说明。

ICustomFormatter定义一种方法,它支持自定义设置对象的值的格式。 ICustomFormatter 接口包含单个方法:ICustomFormatter.Format。当此接口由引用或值类型实现时,Format 方法会返回对象值的自定义格式字符串表示形式。

接口IFormatProvider提供用于检索控制格式化的对象的机制。 类或数值类型实现此接口的 GetFormat方法,以获得提供格式信息或实现类型的处理的对象。IFormatProvider接口同样只包含一个方法。

可能单纯阅读MSDN的说明,还是有点一知半解的感觉。

不过,别急,继续往下看。

下面给出详细代码,看下面...

public class MyFormater:ICustomFormatter,IFormatProvider
{
    public object GetFormat(Type format)
    {
        if (format == typeof(ICustomFormatter))
           return this;
        return null;
    }

    public string Format(string format, object arg, IFormatProvider provider)
    {
        if (format == null)
        {
           if (arg is IFormattable)
              return ((IFormattable)arg).ToString(format, provider);
           return arg.ToString();
        }
        else
        {
            if (format == "MyFormater")
            {
                return "**" + arg.ToString();
            }
            else
            {
                if (arg is IFormattable)
                   return ((IFormattable)arg).ToString(format, provider);
                return arg.ToString();
            }
        }
    }
}
static void Main(string[] args)
{      
     int i = 100;
     string printString;
     MyFormater myFormater = new MyFormater();
     printString = string.Format(myFormater, "{0}", i);
     Console.WriteLine("{0}", printString);
     printString = string.Format(myFormater, "{0:C}", i);
     Console.WriteLine("{0}", printString);
     printString = string.Format(myFormater, "{0:MyFormater}", i);
     Console.WriteLine("{0}", printString);
}

代码中定义了类MyFormater暂且叫作“自定义格式化类”),该类实现了ICustomFormatterIFormatProvider接口所以该类实现了 GetFormatFormat方法其实该类除了实现这两个方法外,也没有完成其他任何工作。

GetFormat方法对传入参数(类型format)进行判断,如果传入的类型formatICustomFormatter就返回类本身,否则返回null。我个人的理解就是:调用MyFormater类的GetFormat方法时,传入类型要求告诉类MyFormater你必须满足类型要求,否则我就不要你了,你要是满足条件,我就要定你了。从MyFormater的角度来思考的话,就是:我(MyFormater就能提供类型为ICustomFormatter的我。举个不恰当的例子,你非得要个人妖,我怎么能给你呢,我可是绝对的纯爷们,呵呵大哭。可见,GetFormat方法主要提供一个满足指定要求的对象,该对象提供格式信息。那该对象提供的格式信息由谁来提供呢,当然是Format方法,msdn对这个有很明确的说明,“Format 方法会返回对象值的自定义格式字符串表示形式。

学习到这里,是不是感觉自己有一丁点的明白了呢。

如果还是一团浆糊,也没事,接着往下看。

不过,到这里,不管你脑子里到底有多浆糊。我都希望你至少能明白一点,那就是GetFormat方法负责返回提供格式信息的对象(这里的MyFormater),而Format 方法负责返回具体的格式信息。说得再简单点,那就是:先得到提供格式化信息的对象,再得到该对象提供的格式化信息

好了,不罗嗦了,继续往下看。

在解释Format方法的具体实现前,先来看看我们的代码到底是怎样运行的。

在Main函数中,定义整型变量i,并初始化为100;定义字符串printString用来保存返回值;实例化MyFormater得到对象myFormater并调用string.Format方法生成printString

printString = string.Format(myFormater, "{0}", i);
printString = string.Format(myFormater, "{0:C}", i);
printString = string.Format(myFormater, "{0:MyFormater}", i);

代码中采用上面三种方式来调用string.Format方法,三者的区别仅在第二个参数。要想弄清楚三者的区别,只有搞清楚string.Format方法到底做了些什么才能知道,所以下面来看看string.Format方法都做了些什么。

在这里,我们需要使用Reflector来查看string.Format方法的源码。

string.Format方法的代码如下:

public static string Format(IFormatProvider provider, string format, params object[] args)
{
    if ((format == null) || (args == null))
    {
        throw new ArgumentNullException((format == null) ? "format" : "args");
    }
    StringBuilder sb = StringBuilderCache.Acquire(format.Length + (args.Length * 8));
    sb.AppendFormat(provider, format, args);
    return StringBuilderCache.GetStringAndRelease(sb);
}

结合我们的函数调用阅读函数签名,了解到:string.Format方法的第一个参数接收我们传入的自定义格式化类对象myFormater第二个参数接收格式化字符串format,第三个参数接收需要被格式化的参数集合args,这里仅变量i。

查看string.Format方法体,可以了解到:方法先对传入参数format及args进行非法判断,参数为空时抛出参数空异常。参数正常时,初始化StringBuilder 对象(关于StringBuilderCache.Acquire的使用这里不做过多介绍,自己去研究),接着调用sb.AppendFormat方法,最后通过语句return StringBuilderCache.GetStringAndRelease(sb)返回结果。可见,真正的格式化操作都在sb.AppendFormat方法中完成,这里并不打算给出sb.AppendFormat方法的完整代码,只是给出sb.AppendFormat方法中调用GetFormat 方法和Format 方法的代码。

if (provider != null)
{
       formatter = (ICustomFormatter) provider.GetFormat(typeof(ICustomFormatter)); 
}

代码传入参数typeof(ICustomFormatter),返回ICustomFormatter类型的formatter 。

if (formatter != null)
{
    if (builder != null)
    {
        str = builder.ToString();       
    }
    str2 = formatter.Format(str, arg, provider);
}

上面就是代码中调用Format方法的地方。

执行上面代码都会去执行我们的自定义格式化类MyFormater的代码。

要想了解详细的执行情况,那就请代开你的VS,开始敲代码吧。

好像到这里,我也没有把问题很好的解释清楚。

这与我对.NET库代码一窍不通是有很大关系的。其实,到这里,我对sb.AppendFormat方法的具体操作也还是停留在了解的层面上,所以就没有过多叙述了。希望大家能谅解。但是到最后,大家至少对我们自定义的格式化类MyFormater在.NET库中是如何被使用的应该有一个初步认识了。

就到这里了。最后给出程序执行结果。看下面...

 

哦,忘了解释Format方法的代码,哎,太累了,就不解释了,大家都是牛人,能看懂的。

88

<think>好的,我需要回答用户关于C#中System.IFormatProvider接口的问题。首先,我得回忆一下这个接口的基本信息。IFormatProvider接口在C#中主要用于格式化和解析数据时提供区域性特定的信息。常见的实现包括CultureInfo类。 接下来,我需要查找这个接口的类型定义。根据引用[3],Convert.ToDouble方法在处理不同参数类型时会使用IFormatProvider。不过用户问的是接口本身的定义,所以应该查看微软的官方文档。IFormatProvider接口通常定义了一个方法:GetFormat(Type formatType),这个方法返回提供格式化信息的对象。 然后,使用场景方面,用户可能想知道什么时候需要用到这个接口。比如在数字、日期格式化时,使用不同的文化设置,如货币符号、日期格式等。引用[5]提到Console.WriteLine使用复合格式字符串,其中可能涉及IFormatProvider来控制格式。比如使用CultureInfo.CurrentCulture作为IFormatProvider的实例,确保格式符合当前区域设置。 另外,引用[2]提到String.Format方法最终会调用一个带有IFormatProvider参数的方法,这说明在字符串格式化时,IFormatProvider用于提供格式化的规则,比如数字的小数点分隔符。 还需要注意自定义格式化的情况。如果用户需要特定格式,可以实现IFormatProviderICustomFormatter接口,自定义格式化逻辑。例如,处理特殊的货币格式或日期表示。 现在,我需要组织这些信息,确保结构清晰。先介绍接口定义,再讲使用场景,最后给出示例。同时,检查是否需要引用提供的资料中的内容。引用[3]、[2]、[5]都涉及到IFormatProvider的应用,可以在适当的地方加入引用标识。 最后,生成相关问题,帮助用户进一步理解。可能的问题包括接口的方法、如何实现自定义格式提供程序,或者与CultureInfo的关系等。确保问题相关且有引导性。</think>### C# System.IFormatProvider 接口详解 #### 1. **类型定义** `System.IFormatProvider` 是 .NET 中用于提供区域性特定格式信息的接口,其核心方法是 `GetFormat`,定义如下: ```csharp public interface IFormatProvider { object? GetFormat(Type? formatType); } ``` 该方法根据传入的 `formatType` 返回对应的格式化对象(如数字、日期格式规则)。例如,`CultureInfo` 类实现了此接口,用于提供本地化的格式信息[^3][^5]。 --- #### 2. **使用场景** ##### (1) **数据格式化与解析** - 用于将数据转换为符合特定区域性规则的字符串,例如: - 数字格式(货币符号、千位分隔符):`double.ToString("C", new CultureInfo("en-US"))`。 - 日期格式:`DateTime.Now.ToString("d", new CultureInfo("fr-FR"))`。 - 在 `Convert.ToDouble`、`DateTime.Parse` 等方法中,可通过 `IFormatProvider` 参数指定解析规则。 ##### (2) **复合格式字符串** - 在 `String.Format` 或 `Console.WriteLine` 中,通过占位符 `{0:N2}` 结合 `IFormatProvider` 控制输出格式: ```csharp double value = 1234.56; Console.WriteLine(String.Format(new CultureInfo("de-DE"), "German format: {0:C}", value)); // 输出 "German format: 1.234,56 €"[^2][^5]。 ``` ##### (3) **自定义格式化** - 实现自定义的 `IFormatProvider` 和 `ICustomFormatter`,可定义特殊格式逻辑(如统一身份证号显示、加密数据脱敏)。 --- #### 3. **示例代码** ##### 使用 CultureInfo 实现本地化格式 ```csharp // 使用美式英语格式 IFormatProvider usFormat = new CultureInfo("en-US"); double number = 1234.56; Console.WriteLine(number.ToString("C", usFormat)); // 输出 "$1,234.56" // 使用德式格式解析日期 string dateStr = "31.12.2023"; DateTime date = DateTime.Parse(dateStr, new CultureInfo("de-DE")); ``` ##### 自定义格式化程序 ```csharp public class CustomFormatter : IFormatProvider, ICustomFormatter { public object GetFormat(Type formatType) { return formatType == typeof(ICustomFormatter) ? this : null; } public string Format(string format, object arg, IFormatProvider formatProvider) { if (arg is double) { return $"Value: {arg:0.000}"; // 强制保留三位小数 } return arg.ToString(); } } // 使用示例 double value = 12.3; Console.WriteLine(String.Format(new CustomFormatter(), "{0}", value)); // 输出 "Value: 12.300" ``` --- #### 4. **与其他类型的关系** - **CultureInfo**:最常用的 `IFormatProvider` 实现,封装语言、区域相关的格式规则。 - **NumberFormatInfo/DateTimeFormatInfo**:专用于数字或日期格式的配置类,可通过 `CultureInfo.NumberFormat` 获取。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值