3.ToString带有自定义格式化参数的理解
上面讲到的ToString都是不带格式化参数的,像 {1:yyyy-MM-dd} 这样的情况是没法处理的,也许有人会说像 DateTime.Now.ToString("yyyy-MM-dd") 这样的情况自己去重载一个ToString方法就可以了,果真如此吗? 下面就测试一下:
{
public string Name { get ; set ; }
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;
}
}
}
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了。
接下来再看看下面的:
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
}
运行一下代码得到预期的结果:
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
}
运行下面的测试代码:
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);
不同的结果的情况,上面的输出结果不同:
这是不好的设计。