[格式化输出] 关于IFormattable, IFormatProvider 和 ICustomFormatter的概念和使用

曾经一度为格式化输出而困惑,看着满天遍野的结构,都不敢去轻易触动。只能使用最安全,但是 低能的object.ToString()方法。终于忍受不了这种窘困的处境,下力气研究一番,也算是有点心得,希望和大家交流一下。

鉴于该格式化输出的结构过于繁琐,我不希望文章陷入条款的解释,于是,我从一个实际问题入手,一步一步地介绍格式化输出的概念,三个接口的意义,以及使用接口的一种模式(Pattern)。

1. PhoneNumber 类

假设我们有如下的一个类,用于存储我们的手机号码。该类提供了简单的ToString()实现,即直接返回含有短线(-)的电话号码。

class  PhoneNumber 
{
   
private string _number = "139-0814-2314";

   
public PhoneNumber() { }
   
public PhoneNumber(string number)
   
{
      _number 
= number;
   }

   
public string Number
   
{
      
get return _number; }
      
set { _number = value; }
   }


   
public override string ToString()
   
{
      
return _number.ToString();
   }

}

2. 提供去掉短线的输出格式 (IFormattable)

现在,我们希望能输出不含有短线的电话号码。一种最简单的办法,就是提供一个属性或者方法来实现。然而,我们希望能够像以货币格式输出整数一样,能够用类似的方法,以不含有短线的方式,输出电话号码。即:

// 我们通过格式字符串(formatString),可以以货币格式输出整数。
int  d  =   100 ;
string  val  =   string .Format( " {0:D} " ,d);
Console.WriteLine(val);
// 结果是(结果会因为区域设置有不同,但结构是类似的,即在数字前会出现货币符号)
// ¥100

// 我们希望能够通过如下的方式,输出不含短线的电话号码。
PhoneNumber pn  =   new  PhoneNumber();
string  val  =   string .Format( " {0:R} " ,pn);
Console.WriteLine(val);
// 期望的结果是:
// 13908142314

我们需要作的,就是实现IFormattable接口:

       class  PhoneNumber : IFormattable
      
{
         
//同上
         public override string ToString()
         
{
            
return ToString(nullnull);

         }


         
IFormattable Members
      }

现在,一切工作的非常良好,我们的PhoneNumber类也非常顺利的发布出去了。一切看起来都非常不错!

3. 提供隐藏部分号码的输出格式 (IFormatProvider, ICustomFormatter)

在使用中,用户打电话问讯我们,能否提供隐藏部分号码的输出格式,即13908142314输出为139****2314。“这个容易,我们马上更新”,放下电话,我们在switch中加入了一项,就能解决问题。可是,这样做,一个潜在的问题是,每次我们得到新的需求,都需要修改我们的PhoneNumber类,这样做,每次用户都需要重新编译PhoneNumber类,以及所有和它相关的类,这样是一个高风险,违背软件设计原则的解决办法。于是,我们应该采用如下的模式来实现扩展的需求:

首先,实现一个单独的HideFormat类,实现两个接口。这个类就是我们自己定义的格式规则。

              class  HideFormat : IFormatProvider, ICustomFormatter
      
{
         
IFormatProvider Members

         
ICustomFormatter Members
      }

然后,修改我们的PhoneNumber类(只需要修改一次)。

       class  PhoneNumber : IFormattable
      
{
         
//同上

         
IFormattable Members
      }

然后,为了输出隐藏部分号码的格式。我们只需要做如下的调用:

PhoneNumber pn  =   new  PhoneNumber();
SecureFormat sf 
=   new  SecureFormat();

string  _val;
_val 
=   string .Format(sf, " {0:H} " , pn);
Console.WriteLine(_val);  
// 输出结果是139****2314
   
_val 
=  pn.ToString( " H " ,sf);
Console.WriteLine(_val); 
// 输出结果是139****2314

自此,我们完成了所有的任务和更新。最后,让我们看看扩展性。如果以后我们接到了新的格式申请,我们需要作的,就是实现一个类似SecureFormat的自定义的格式类,然后交给用户使用。不需要我们修改PhoneNumber的任何实现。这个解决方案,很好的体现了开放-闭合(Open-Close Principle)的软件设计原则。

作者:Shu Liu(本人系.NET菜鸟,希望大家给予意见和建议)

Mail: shuliu@microsoft.com

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值