建议13: 为类型输出格式化字符串

有两种方法可以为类型提供格式化的字符串输出。一种是意识到类型会产生格式化字符串输出,于是让类型继承接口IFormattable。这对类型来 说,是一种主动实现的方式,要求开发者可以预见类型在格式化方面的要求。更多的时候,类型的使用者需为类型自定义格式化器,这就是第二种方法,也是最灵活 多变的方法,可以根据需求的变化为类型提供多个格式化器。下面就来详细介绍这两种方法。

最简单的字符串输出是为类型重写ToString方法,如果没有为类型重写该方法,默认会调用Object的ToString方法,它会返回当前类型的类型名称。但即使是重写了ToString方法,提供的字符串输出也是非常单一的,而通过实现IFormattable接口的ToString方法, 可以让类型根据用户的输入而格式化输出。如下面这个类型Person,它本身提供了属性FirstName和LastName。现在,根据中文和英文语言 地区的习惯,提供的ToString方法要支持输出“Hu Jessica”或 “Jessica Hu”,实现代码应该如下所示:

    class Person : IFormattable
{
public string IDCode { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; } //实现接口IFormattable的方法ToString
public string ToString(string format, IFormatProvider formatProvider)
{
switch (format)
{
case "Ch":
return this.ToString();
case "Eg":
return string.Format("{0} {1}", FirstName, LastName);
default:
return this.ToString();
}
} //重写Object.ToString()
public override string ToString()
{
return string.Format("{0} {1}", LastName, FirstName);
}
}

调用者代码如下所示:

    Person person = new Person() { FirstName = "Jessica", LastName = "Hu",
IDCode = "NB123" };
Console.WriteLine(person);
Console.WriteLine(person.ToString("Ch", null));
Console.WriteLine(person.ToString("Eg", null));

输出为:

    Hu Jessica
Hu Jessica
Jessica Hu

上面这种方法是在意识到类型会存在格式化字符串输出方面的需求时,提前为类型继承了接口IFormattable。如果类型本身没有提供格 式化输出的功能,这个时候,格式化器就派上了用场。格式化器的好处就是可以根据需求的变化,随时增加或者修改它。假设Person类如以下所示的实现。

    class Person
{
public string IDCode { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
}

针对Person的格式化器的实现为:

    class PersonFomatter : IFormatProvider, ICustomFormatter
{ #region IFormatProvider 成员 public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
} #endregion #region ICustomFormatter 成员 public string Format(string format, object arg, IFormatProvider formatProvider)
{
Person person = arg as Person;
if (person == null)
{
return string.Empty;
} switch (format)
{
case "Ch":
return string.Format("{0} {1}", person.LastName,
person.FirstName);
case "Eg":
return string.Format("{0} {1}", person.FirstName,
person.LastName);
case "ChM":
return string.Format("{0} {1} : {2}", person.LastName,
person.FirstName, person.IDCode);
default:
return string.Format("{0} {1}", person.FirstName,
person.LastName);
}
} #endregion
}

一个典型的格式化器应该继承接口IFormatProvider和ICustomFomatter,所以应该像下面这样调用格式化器:

    Person person = new Person() { FirstName = "Jessica", LastName = "Hu", IDCode = "NB123" };
Console.WriteLine(person.ToString());
PersonFomatter pFormatter = new PersonFomatter();
Console.WriteLine(pFormatter.Format("Ch", person, null));
Console.WriteLine(pFormatter.Format("Eg", person, null));
Console.WriteLine(pFormatter.Format("ChM", person, null));

输出为:

    ConsoleApplication4.Person
Hu Jessica
Jessica Hu
Hu Jessica : NB123

本示例也演示了如果没有重写Object.ToString方法,类型会输出类型名称。

实际上,在第一个版本的Person类型中,如果对IFormattable的ToString方法稍作修改,就能让格式化输出在语法上支持更多的调用方式。注意看Person类型的最终版本中ToString方法的switch结构的default部分,如下所示。

    class Person : IFormattable
{
public string IDCode { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; } //实现接口IFormattable的方法ToString
public string ToString(string format, IFormatProvider formatProvider)
{
switch (format)
{
case "Ch":
return this.ToString();
case "Eg":
return string.Format("{0} {1}", FirstName, LastName);
default:
//return this.ToString();
ICustomFormatter customFormatter = formatProvider as ICustomFormatter;
if (customFormatter == null)
{
return this.ToString();
}
return customFormatter.Format(format, this, null);
}
} //重写Object.ToString()
public override string ToString()
{
return string.Format("{0} {1}", LastName, FirstName);
}
}

最终,调用者的代码能够支持如下所示的语法:

    Person person = new Person() { FirstName = "Jessica", LastName = "Hu", IDCode = "NB123" };
Console.WriteLine(person.ToString());
PersonFomatter pFormatter = new PersonFomatter();
//第一类格式化输出语法
Console.WriteLine(pFormatter.Format("Ch", person, null));
Console.WriteLine(pFormatter.Format("Eg", person, null));
Console.WriteLine(pFormatter.Format("ChM", person, null));
//第二类格式化输出语法,更简洁
Console.WriteLine(person.ToString("Ch", pFormatter));
Console.WriteLine(person.ToString("Eg", pFormatter));
Console.WriteLine(person.ToString("ChM", pFormatter));

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

编写高质量代码改善C#程序的157个建议——建议13: 为类型输出格式化字符串的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  2. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  3. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  4. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  5. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  6. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  7. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

  8. 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释

    建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...

  9. 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量

    建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...

  10. 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法

    建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...

随机推荐

  1. YII框架安装过程-数据库访问

    1.电脑上原来安装了phpstudy.关掉phpstudy,启动wamp,虽启动成功,但仍然无法使用phpmyadmin登录数据库管理页面. 2.查看到系统服务有mysql服务,检查属性均为emsoa ...

  2. 项目中使用的图片上传方法,base64存本地

    //生成健康报告 public function uploadJkbg(Request $r) { $data = $r->all(); $jkbg['jkbg_ctime'] = time() ...

  3. laravel 网站速率优化

    https://segmentfault.com/a/1190000009954966

  4. Fragment和FragmentActivity的使用

    可以分为下面的几部分: 使用支持库 创建一个Fragment 创建一个动态UI 多个Fragment之间的通信 1.使用支持库 如果您的应用需要运行在3.0及以上的版本,可以忽略这部分内容. 如果您的 ...

  5. win7系统清除USBSTOR记录

    方法一 1.Win+R,出现运行窗口,如图所示: 2.在输入框中输入“regedit”,如图所示: 3.进入后,点击编辑-查找,查找输入框中输入“USBSTOR”(为了加快查找速度,可以只选择“项”) ...

  6. SQL2005 如何在没有日志文件的情况下如何恢复MDF数据库文件?

    第一步:先建立一个同名数据库,停止SQL SERVER2005,将没有日志的的.mdf数据库文件覆盖刚新建的.mdf数据库文件,重新启动数据库. 第二步:在查询分析器中运行如下代码(将数据库名修改为您 ...

  7. ssh-copy-id:/usr/bin/ssh-copy-id: ERROR: No identities found

    $ ssh-copy-id remote-machine 公钥,私钥已经生成,执行上述命令完毕出现如下错误: /usr/bin/ssh-copy-id: ERROR: No identities fo ...

  8. SQL万能密码:' or 1='1

    select name,pass from tbAdmin where name='admin' and pass='123456' 输入用户名:' or 1='1 SQL变成下面这个样子: sele ...

  9. 【转】使用Badboy录制脚本,作为JMeter测试的素材

    接触Badboy,是因为JMeter要引用Badboy导出的脚本 Badboy的录制提供两个模式:Request(默认模式) 和navigation模式.点击下图N,切换模式:但是要导出到Jmeter ...

  10. js操作一般文件和csv文件

    js操作一般文件和csv文件 将文本文件读成字符串 <input type="file" id="upload"> document.getElem ...