新聞中心
ToString(string format)擴(kuò)展主要是String類(lèi)的擴(kuò)展,這個(gè)擴(kuò)展在很多時(shí)候都能用得到,合理重寫(xiě)的ToString(string format)擴(kuò)展方法中編程、調(diào)試中給我們很大方便。

成都創(chuàng)新互聯(lián)公司是一家集網(wǎng)站建設(shè),屏南企業(yè)網(wǎng)站建設(shè),屏南品牌網(wǎng)站建設(shè),網(wǎng)站定制,屏南網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,屏南網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力??沙浞譂M(mǎn)足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶(hù)成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
在.Net中,System.Object.ToString()是用得最多的方法之一,ToString()方法在Object類(lèi)中被定義為virtual,Object類(lèi)給了它一個(gè)默認(rèn)實(shí)現(xiàn):
1 public virtual string ToString()
2 {
3 return this.GetType().ToString();
4 }
.NET中原生的class或struct,如int,DateTime等都對(duì)它進(jìn)行重寫(xiě)(override),以讓它返回更有價(jià)值的值,而不是類(lèi)型的名稱(chēng)。合理重寫(xiě)的ToString()方法中編程、調(diào)試中給我們很大方便。但終究一個(gè)類(lèi)只有一個(gè)ToString()方法,不能滿(mǎn)足我們多樣化的需求,很多類(lèi)都對(duì)ToString()進(jìn)行了重載。如下:
1 string dateString = DateTime.Now.ToString("yyyy"); //2009
2 string intString = 10.ToString("d4"); //0010
int、DateTime都實(shí)現(xiàn)了ToString(string format)方法,極大方便了我們的使用。
對(duì)于我們自己定義的類(lèi)型,我們也應(yīng)該提供一個(gè)合理的ToString()重寫(xiě),如果能夠提供再提供一個(gè)ToString(string format),就會(huì)令我們后期的工作更加簡(jiǎn)單。試看以下類(lèi)型:
1 public class People
2 {
3 private List
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 [[5735]]
21 }
一個(gè)簡(jiǎn)單的類(lèi),我們給出一個(gè)ToString()重寫(xiě),返回包含Id和Name兩個(gè)關(guān)鍵屬性的字符串?,F(xiàn)在我們需要一個(gè)ToString(string format)重寫(xiě),以滿(mǎn)足以下應(yīng)用:
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("編號(hào)為:Id,姓名:Name"); //理想輸出:編號(hào)為:1,姓名:鶴沖天
想想怎么實(shí)現(xiàn)吧,記住format是可變的,不定使用了什么屬性,也不定進(jìn)行了怎樣的組合...
也許一個(gè)類(lèi)好辦,要是我們定義很多類(lèi),幾十、幾百個(gè)怎么辦?一一實(shí)現(xiàn)ToString(string format)會(huì)把人累死的。好在我們有擴(kuò)展方法,我們對(duì)object作一擴(kuò)展ToString(string format),.Net中object是所有的基類(lèi),對(duì)它擴(kuò)展后所有的類(lèi)都會(huì)自動(dòng)擁有了。當(dāng)然已有ToString(string format)實(shí)現(xiàn)的不會(huì),因?yàn)樵椒ǖ膬?yōu)先級(jí)高,不會(huì)被擴(kuò)展方法覆蓋掉。
來(lái)看如何實(shí)現(xiàn)吧(我們會(huì)一步一步改進(jìn),為區(qū)分各個(gè)版本,分別擴(kuò)展為T(mén)oString1、ToString2...分別對(duì)應(yīng)版本一、版本二...):
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行通過(guò)反射獲取了公有的、實(shí)例的Get屬性(如果需要靜態(tài)的或私有的,修改第5行中即可),7~8行動(dòng)態(tài)生成一個(gè)正則表達(dá)式來(lái)匹配format,10~16行是匹配成功后的處理。這里用到反射和正則表達(dá)式,如果不熟悉不要緊,先調(diào)試運(yùn)行吧,測(cè)試一下前面剛提到的應(yīng)用:
第一個(gè)和我們理想的有點(diǎn)差距,就是日期上,我們應(yīng)該給日期加上"yyyy-MM-dd"的格式,這個(gè)我們稍后改進(jìn),我們現(xiàn)在有一個(gè)更大的問(wèn)題:
如果我們想輸出:“People: Id 1, Name 鶴沖天”,format怎么寫(xiě)呢?寫(xiě)成format="People: Id Id, Name Name",這樣沒(méi)法處理了,format中兩個(gè)Id、兩個(gè)Name,哪個(gè)是常量,哪個(gè)是變量???解決這個(gè)問(wèn)題,很多種方法,如使用轉(zhuǎn)義字符,可是屬性長(zhǎng)了不好寫(xiě),如format="\B\r\i\t\h\d\a\y Brithday"。我權(quán)衡了一下,最后決定采用類(lèi)似Sql中對(duì)字段名的處理方法,在這里就是給變量加上中括號(hào),如下:
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]");
版本二的實(shí)現(xiàn)代碼如下:
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, @"\[(? [^\]]+)\] ", evaluator, RegexOptions.Compiled);
20 }
調(diào)試執(zhí)行一下:
與版本一類(lèi)似,不過(guò)這里沒(méi)有動(dòng)態(tài)構(gòu)建正則表達(dá)式,因?yàn)橛辛酥欣ㄌ?hào),很容易區(qū)分常量和變量,所以我們通過(guò)“屬性名”來(lái)找“屬性”(對(duì)應(yīng)代碼中第10行)。如果某個(gè)屬性找不到,我們會(huì)將這“[Name]”原樣返回(對(duì)就第17行)。另一種做法是拋出異常,我不建議拋異常,在ToString(string format)是不合乎“常理”的。
版本二相對(duì)版本一效率有很大提高,主要是因?yàn)榘姹径皇褂靡粋€(gè)簡(jiǎn)單的正則表達(dá)式:@"\[(? [^\]]+)\]"。而版本一中的如果被擴(kuò)展類(lèi)的屬性特別多,動(dòng)態(tài)生成的正則表達(dá)式會(huì)很長(zhǎng),執(zhí)行起來(lái)也會(huì)相對(duì)慢。
我們現(xiàn)在來(lái)解決兩個(gè)版本中都存在的時(shí)間日期格式問(wèn)題,把時(shí)間日期格式"yyyy-MM-dd"也放入中括號(hào)中,測(cè)試代碼如下:
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]");
版本三實(shí)現(xiàn)代碼:
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 = @"\[(? [^\[\]:]+)(\s*:\s*(? [^\[\]:]+))?\] ";
23 return Regex.Replace(format, pattern, evaluator, RegexOptions.Compiled);
24 }
測(cè)試一下,可OK了:
對(duì)于簡(jiǎn)單的值類(lèi)型屬性沒(méi)問(wèn)題了,但對(duì)于復(fù)雜一些類(lèi)型如,如People的屬性Son(Son就是兒子,我一開(kāi)始寫(xiě)成了Sun),他也是一個(gè)People類(lèi)型,他也有屬性的,而且他也可能有Son...
先看下調(diào)用代碼吧:
1 People p4 = new People { Id = 1, Name = "鶴沖天", Brithday = new DateTime(1990, 9, 9) };
2 p4.Son = new People { Id = 2, Name = "鶴小天", Brithday = new DateTime(2015, 9, 9) };
3 p4.Son.Son = new People { Id = 3, Name = "鶴微天", Brithday = new DateTime(2040, 9, 9) };
4 string s4 = p4.ToString4("[Name] 的孫子 [Son.Son.Name] 的生日是:[Son.Son.Brithday: yyyy年MM月dd日]。");
“鶴沖天”也就是我了,有個(gè)兒子叫“鶴小天”,“鶴小天”有個(gè)兒子,也就是我的孫子“鶴微天”。哈哈,祖孫三代名字都不錯(cuò)吧(過(guò)會(huì)先把小天、微天這兩個(gè)名字注冊(cè)了)!主要看第4行,format是怎么寫(xiě)的。下面是版本四實(shí)現(xiàn)代碼,由版本三改進(jìn)而來(lái):
1 public static string ToString4(this object obj, string format)
2 {
3 MatchEvaluator evaluator = match =>
4 {
5 string[] propertyNames = match.Groups["Name"].Value.Split('.');
6 string propertyFormat = match.Groups["Format"].Value;
7
8 object propertyValue = obj;
9 try
10 {
11 foreach (string propertyName in propertyNames)
12 propertyValue = propertyValue.GetPropertyValue(propertyName);
13 }
14 catch
15 {
16 return match.Value;
17 }
18
19 if (string.IsNullOrEmpty(format) == false)
20 return string.Format("{0:" + propertyFormat + "}", propertyValue);
21 else return propertyValue.ToString();
22 };
23 string pattern = @"\[(? [^\[\]:]+)(\s*[:]\s*(? [^\[\]:]+))?\] ";
24 return Regex.Replace(format, pattern, evaluator, RegexOptions.Compiled);
25 }
為了反射獲取屬性方法,用到了GetPropertyValue擴(kuò)展如下(版本三的實(shí)現(xiàn)用上這個(gè)擴(kuò)展會(huì)更簡(jiǎn)潔)(考慮性能請(qǐng)?jiān)诖朔椒泳彺妫?/p>
1 public static object GetPropertyValue(this object obj, string propertyName)
2 {
3 Type type = obj.GetType();
4 PropertyInfo info = type.GetProperty(propertyName);
5 return info.GetValue(obj, null);
6 }
先執(zhí)行,再分析:
執(zhí)行正確! 版本四,8~17行用來(lái)層層獲取屬性。也不太復(fù)雜,不多作解釋了。說(shuō)明一下,版本四是不完善的,沒(méi)有做太多處理。
我們最后再來(lái)看一下更復(fù)雜的應(yīng)用,Peoplee有Friends屬性,這是一個(gè)集合屬性,我們想獲取朋友的個(gè)數(shù),并列出朋友的名字,如下:
1 People p5 = new People { Id = 1, Name = "鶴沖天"};
2 p5.AddFriend(new People { Id = 11, Name = "南霸天" });
3 p5.AddFriend(new People { Id = 12, Name = "日中天" });
4 string s5 = p5.ToString5("[Name] 目前有 [Friends: .Count] 個(gè)朋友:[Friends: .Name]。");
注意,行4中的Count及Name前都加了個(gè)小點(diǎn),表示是將集合進(jìn)行操作,這個(gè)小點(diǎn)是我看著方便自己定義的。再來(lái)看實(shí)現(xiàn)代碼,到版本五了:
1 public static string ToString5(this object obj, string format)
2 {
3 MatchEvaluator evaluator = match =>
4 {
5 string[] propertyNames = match.Groups["Name"].Value.Split('.');
6 string propertyFormat = match.Groups["Format"].Value;
7
8 object propertyValue = obj;
9
10 try
11 {
12 foreach (string propertyName in propertyNames)
13 propertyValue = propertyValue.GetPropertyValue(propertyName);
14 }
15 catch
16 {
17 return match.Value;
18 }
19
20 if (string.IsNullOrEmpty(propertyFormat) == false)
21 {
22 if (propertyFormat.StartsWith("."))
23 {
24 string subPropertyName = propertyFormat.Substring(1);
25 IEnumerable
執(zhí)行結(jié)果:
比較不可思議吧,下面簡(jiǎn)單分析一下。行22~行33是對(duì)集合進(jìn)行操作的相關(guān)處理,這里只是簡(jiǎn)單實(shí)現(xiàn)了Count,當(dāng)然也可以實(shí)現(xiàn)Min、Max、Sum、Average等等?!?Name”這個(gè)表示方法不太好,這里主要是為了展示,大家能明白了就好。
本文來(lái)自鶴沖天的博客園文章《c#擴(kuò)展方法奇思妙用高級(jí)篇五:ToString(string format) 擴(kuò)展》
【編輯推薦】
- DropDownList顯示的C#遞歸實(shí)現(xiàn)淺析
- C#treeview遞歸操作數(shù)據(jù)庫(kù)淺析
- C#遞歸樹(shù)實(shí)現(xiàn)實(shí)例簡(jiǎn)析
- C#打開(kāi)記事本實(shí)現(xiàn)實(shí)例解析
- C#調(diào)用記事本實(shí)例淺析
名稱(chēng)欄目:C#中ToString(string format)擴(kuò)展的妙用
本文網(wǎng)址:http://www.5511xx.com/article/dhodgdd.html


咨詢(xún)
建站咨詢(xún)
