彻底学通string.Format以及IFormattable,IFormatProvider,ICustomFormatter(2)

 3.ToString带有自定义格式化参数的理解

  上面讲到的ToString都是不带格式化参数的,像  {1:yyyy-MM-dd} 这样的情况是没法处理的,也许有人会说像 DateTime.Now.ToString("yyyy-MM-dd") 这样的情况自己去重载一个ToString方法就可以了,果真如此吗? 下面就测试一下:

public class PersonWithToString
    {
        
public string Name { getset; }

        
public override string ToString()
        {
            
return Name;
        }

        
public string ToString(string format)
        {
            
switch (format)
            {
                
case "UPP":
                    
return Name.ToUpper();
                
case "LOW":
                    
return Name.ToLower();
                
default:
                    
return Name;
            }
        }
    }
var msg9 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.",
                                  
new PersonWithToString() { Name = "Zhezhe" }.ToString("UPP"), DateTime.Now, DateTime.Now.DayOfWeek);
            Console.WriteLine(msg9);

  msg9的实际输出为 Hello Cnblogs, I am ZHEZHE,Today is 2010-07-30 Friday.  这个正是我们需要的,当然,这个肯定是对的,要不然就是.net的bug了。

  接下来再看看下面的:

var msg8 = string.Format("Hello Cnblogs, I am {0:UPP},Today is {1:yyyy-MM-dd} {2}.",
                                  
new PersonWithToString() { Name = "Zhezhe" }, DateTime.Now, DateTime.Now.DayOfWeek);

  实际输出是: Hello Cnblogs, I am Zhezhe,Today is 2010-07-30 Friday.  并不是我们所期望的。实际上上面的代码是调用了PersonWithToString类的不带参数的ToString()方法。言外之意就是  {0:UPP}这样的格式实际上内部处理的是和  {0}一样的效果了。在1中提到了分解的原理用了类似两个字,实际情况并不是这么简单。

  {0:UPP} 真正调用的方法签名是    string ToString(string format,IFormatProvider formatProvider)

  而且也不是直接调用该对象的此方法。而是通过 IFormattable 接口实现的方法。

  现在定义实现了该接口的 Person2类。  

public class Person2 : IFormattable
{
public string Name { get; set; }

public override string ToString()
{
return Name;
}

#region IFormattable Members

public string ToString(string format, IFormatProvider formatProvider)
{
if (string.IsNullOrEmpty(format))
return ToString();

switch (format)
{
case "UPP":
return Name.ToUpper();
case "LOW":
return Name.ToLower();
default:
return Name;
}
}

#endregion
}

    运行一下代码得到预期的结果:

  //使用实现了IFormattable接口的Person2对象
var msg10 = string.Format("Hello Cnblogs, I am {0:UPP},Today is {1:yyyy-MM-dd} {2}.",
new Person2() { Name = "ZhezheToUpper" }, DateTime.Now, DateTime.Now.DayOfWeek);
Console.WriteLine(msg10);

   ZhezheToUpper已经输出成全部大写形式了。

  既然{0:UPP}会调用接口定义的ToString方法,那么{0}呢? 如果该类没有实现IFormattable接口,上面已经说了,会调用重载的或者是基类的ToString()方法。但是如果该类已经实现了IFormattable接口,那么{0}也不会去调用重载的或者是基类的ToString()方法了,它始终是去调用 接口定义的 ToString方法。下面具体印证一下:

public class Person3 : IFormattable
{
public string Name { get; set; }

public override string ToString()
{
return Name;
}

#region IFormattable Members

public string ToString(string format, IFormatProvider formatProvider)
{
if (string.IsNullOrEmpty(format))
return Name + " IFormattable Method";

switch (format)
{
case "UPP":
return Name.ToUpper();
case "LOW":
return Name.ToLower();
default:
return Name + " IFormattable Method";
}
}

#endregion
}

  运行下面的测试代码:

var msg11 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.",
                                  
new Person3() { Name = "ZhezheToUpper" }, DateTime.Now, DateTime.Now.DayOfWeek);
            Console.WriteLine(msg11); 

  输出为: Hello Cnblogs, I am ZhezheToUpper IFormattable Method,Today is 2010-07-30 Friday。

  证明了确实是调用了接口定义的方法,而不是重载的ToString方法,否则是输出ZhezheToUpper

  再来看一下Person2中实现的ToString方法:

   if (string.IsNullOrEmpty(format))
          return ToString();
  如果是刚才的{0}不带格式化参数的调用,则format参数传过来的是null值,这里需要自己判断,如果是null值,一般情况下是手工去调用重载的ToString()方法。
  所以Person2的做法是好的,而Person3中的做法是不好的,Person3只是为了测试分辨出调用的是哪个方法才这么设计的。

  总结:一.对于实现IFormattable 接口时,如果format参数为null(即不带格式化参数的情况,如{0})则应该调用重载的 ToString()方法,而不应该自己去另外写代码。

       二.如果找不到相应的格式化参数,例如{0:AAA},在Person2的switch中并无匹配的AAA,这种情况一般也应该去调用重载的 ToString()方法。

  否则就会出: 

 //以下两个输出结果不一样,是不合理的
var msg12 = string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.",new Person3() { Name = "ZhezheToUpper" }, DateTime.Now, DateTime.Now.DayOfWeek);
Console.WriteLine(msg12);

var msg13 
= string.Format("Hello Cnblogs, I am {0},Today is {1:yyyy-MM-dd} {2}.",new Person3() { Name = "ZhezheToUpper" }.ToString(), DateTime.Now, DateTime.Now.DayOfWeek);
Console.WriteLine(msg13);

  不同的结果的情况,上面的输出结果不同:

  这是不好的设计。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页