c#扩展方法奇思妙用高级篇五:ToString(string format) 扩展2010-04-23 博客园 鹤冲天在.Net中,System.Object.ToString()是用得最多的方法之一,ToString()方法在Object类中被定义为virtual,Object类给了它一个默认实现:
1 public virtual string ToString()
2 {
3 return this.GetType().ToString();
4 }
.Net中原生的class或struct,如int,DateTime等都对它进行重写(override),以让它返回更有价值的值,而不是类型的名称。合理重写的ToString()方法中编程、调试中给我们很大方便。但终究一个类只有一个ToString()方法,不能满足我们多样化的需求,很多类都对ToString()进行了重载。如下:
1 string dateString = DateTime.Now.ToString("yyyy"); //2009
2 string intString = 10.ToString("d4"); //0010
int、DateTime都实现了ToString(string format)方法,极大方便了我们的使用。对于我们自己定义的类型,我们也应该提供一个合理的ToString()重写,如果能够提供再提供一个ToString(string format),就会令我们后期的工作更加简单。试看以下类型:
1 public class People
2 {
3 private List<People> friends = new List<People>();
4
5 public int Id { get; set; }
6 public string Name { get; set; }
7 public DateTime Brithday { get; set; }
8 public People Son { get; set; }
9 public People[] Friends { get { return friends.ToArray(); } }
10
11 public void AddFriend(People newFriend)
12 {
13 if (friends.Contains(newFriend)) throw new ArgumentNullException("newFriend", "该朋友已添加");
14 else friends.Add(newFriend);
15 }
16 public override string ToString()
17 {
18 return string.Format("Id: {0}, Name: {1}", Id, Name);
19 }
20 
21 }
一个简单的类,我们给出一个ToString()重写,返回包含Id和Name两个关键属性的字符串。现在我们需要一个ToString(string format)重写,以满足以下应用:
1 People p = new People { Id = 1, Name = "鹤冲天", Brithday = new DateTime(1990, 9, 9) };
2 string s0 = p.ToString("Name 生日是 Brithday"); //理想输出:鹤冲天 生日是 1990-9-9
3 string s1 = p.ToString("编号为:Id,姓名:Name"); //理想输出:编号为:1,姓名:鹤冲天
想想怎么实现吧,记住format是可变的,不定使用了什么属性,也不定进行了怎样的组合...也许一个类好办,要是我们定义很多类,几十、几百个怎么办?一一实现ToString(string format)会把人累死的。好在我们有扩展方法,我们对object作一扩展ToString(string format),.Net中object是所有的基类,对它扩展后所有的类都会自动拥有了。当然已有ToString(string format)实现的不会,因为原生方法的优先级高,不会被扩展方法覆盖掉。来看如何实现吧(我们会一步一步改进,为区分各个版本,分别扩展为ToString1、ToString2...分别对应版本一、版本二...):
1 public static string ToString1(this object obj, string format)
2 {
3 Type type = obj.GetType();
4 PropertyInfo[] properties = type.GetProperties(
5 BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Instance);
6
7 string[] names = properties.Select(p => p.Name).ToArray();
8 string pattern = string.Join("|", names);
9
10 MatchEvaluator evaluator = match =>
11 {
12 PropertyInfo property = properties.First(p => p.Name == match.Value);
13 object propertyValue = property.GetValue(obj, null);
14 if (propertyValue != null) return propertyValue.ToString();
15 else return "";
16 };
17 return Regex.Replace(format, pattern, evaluator);
18 }
3~5行通过反射获取了公有的、实例的Get属性(如果需要静态的或私有的,修改第5行中即可),7~8行动态生成一个正则表达式来匹配format,10~16行是匹配成功后的处理。这里用到反射和正则表达式,如果不熟悉不要紧,先调试运行吧,测试一下前面刚提到的应用: