第一個和我們理想的有點差距,就是日期上,我們應該給日期加上"yyyy-MM-dd"的格式,這個我們稍後改進,我們現在有一個更大的問題:
如果我們想輸出:“People: Id 1, Name 鶴沖天”,format怎麼寫呢?寫成format="People: Id Id, Name Name",這樣沒法處理了,format中兩個Id、兩個Name,哪個是常量,哪個是變量啊?解決這個問題,很多種方法,如使用轉義字符,可是屬性長了不好寫,如format="\B\r\i\t\h\d\a\y Brithday"。我權衡了一下,最後決定采用類似Sql中對字段名的處理方法,在這裡就是給變量加上中括號,如下:
1 People p2 = new People { Id = 1, Name = "鶴沖天", Brithday = new DateTime(1990, 9, 9) };
2 string s2 = p1.ToString2("People:Id [Id], Name [Name], Brithday [Brithday]");
版本二的實現代碼如下:
1 public static string ToString2(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 MatchEvaluator evaluator = match =>
8 {
9 string propertyName = match.Groups["Name"].Value;
10 PropertyInfo property = propertIEs.FirstOrDefault(p => p.Name == propertyName);
11 if (property != null)
12 {
13 object propertyValue = property.GetValue(obj, null);
14 if (propertyValue != null) return propertyValue.ToString();
15 else return "";
16 }
17 else return match.Value;
18 };
19 return Regex.Replace(format, @"\[(?<Name>[^\]]+)\]", evaluator, RegexOptions.Compiled);
20 }
調試執行一下:
與版本一類似,不過這裡沒有動態構建正則表達式,因為有了中括號,很容易區分常量和變量,所以我們通過“屬性名”來找“屬性”(對應代碼中第10行)。如果某個屬性找不到,我們會將這“[Name]”原樣返回(對就第17行)。另一種做法是拋出異常,我不建議拋異常,在ToString(string format)是不合乎“常理”的。
版本二相對版本一效率有很大提高,主要是因為版本二只使用一個簡單的正則表達式:@"\[(?<Name>[^\]]+)\]"。而版本一中的如果被擴展類的屬性特別多,動態生成的正則表達式會很長,執行起來也會相對慢。
我們現在來解決兩個版本中都存在的時間日期格式問題,把時間日期格式"yyyy-MM-dd"也放入中括號中,測試代碼如下:
1 People p3 = new People { Id = 1, Name = "鶴沖天", Brithday = new DateTime(1990, 9, 9) };
2 string s3 = p3.ToString3("People:Id [Id: d4], Name [Name], Brithday [Brithday: yyyy-MM-dd]");
版本三實現代碼:
1 public static string ToString3(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 MatchEvaluator evaluator = match =>
8 {
9 string propertyName = match.Groups["Name"].Value;
10 string propertyFormat = match.Groups["Format"].Value;
11
12 PropertyInfo propertyInfo = propertIEs.FirstOrDefault(p => p.Name == propertyName);
13 if (propertyInfo != null)
14 {
15 object propertyValue = propertyInfo.GetValue(obj, null);
16 if (string.IsNullOrEmpty(propertyFormat) == false)
17 return string.Format("{0:" + propertyFormat + "}", propertyValue);
18 else return propertyValue.ToString();
19 }
20 else return match.Value;
21 };
22 string pattern = @"\[(?<Name>[^\[\]:]+)(\s*:\s*(?<Format>[^\[\]:]+))?\]";
23 return Regex.Replace(format, pattern, evaluator, RegexOptions.Compiled);
24 }
測試一下,可OK了: