成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

C#中ToString(string format)擴展的妙用

開發 后端
這里將介紹C#中ToString(string format)擴展的妙用,在.NET中,System.Object.ToString()是用得最多的方法之一。
ToString(string format)擴展主要是String類的擴展,這個擴展在很多時候都能用得到,合理重寫的ToString(string format)擴展方法中編程、調試中給我們很大方便。

在.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 { getset; }
 6         public string Name { getset; }
 7         public DateTime Brithday { getset; }
 8         public People Son { getset; }
 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     }

 一個簡單的類,我們給出一個ToString()重寫,返回包含Id和Name兩個關鍵屬性的字符串?,F在我們需要一個ToString(string format)重寫,以滿足以下應用:

1     People p = new People { Id = 1, Name = "鶴沖天", Brithday = new DateTime(199099) };
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 != nullreturn propertyValue.ToString();
15                 else return "";
16             };
17         return Regex.Replace(format, pattern, evaluator);
18     }

 3~5行通過反射獲取了公有的、實例的Get屬性(如果需要靜態的或私有的,修改第5行中即可),7~8行動態生成一個正則表達式來匹配format,10~16行是匹配成功后的處理。這里用到反射和正則表達式,如果不熟悉不要緊,先調試運行吧,測試一下前面剛提到的應用:

Get屬性

 第一個和我們理想的有點差距,就是日期上,我們應該給日期加上"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(199099) };
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 != nullreturn propertyValue.ToString();
15                else return "";
16            }
17            else return match.Value;
18        };
19        return Regex.Replace(format, @"\[(?[^\]]+)\]", evaluator, RegexOptions.Compiled);
20    }

調試執行一下:

調試執行

與版本一類似,不過這里沒有動態構建正則表達式,因為有了中括號,很容易區分常量和變量,所以我們通過“屬性名”來找“屬性”(對應代碼中第10行)。如果某個屬性找不到,我們會將這“[Name]”原樣返回(對就第17行)。另一種做法是拋出異常,我不建議拋異常,在ToString(string format)是不合乎“常理”的。 

 版本二相對版本一效率有很大提高,主要是因為版本二只使用一個簡單的正則表達式:@"\[(?[^\]]+)\]"。而版本一中的如果被擴展類的屬性特別多,動態生成的正則表達式會很長,執行起來也會相對慢。
 

 我們現在來解決兩個版本中都存在的時間日期格式問題,把時間日期格式"yyyy-MM-dd"也放入中括號中,測試代碼如下:

1     People p3 = new People { Id = 1, Name = "鶴沖天", Brithday = new DateTime(199099) };
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 = @"\[(?[^\[\]:]+)(\s*:\s*(?[^\[\]:]+))?\]";
23         return Regex.Replace(format, pattern, evaluator, RegexOptions.Compiled);
24     }

測試一下,可OK了:

運行完成

對于簡單的值類型屬性沒問題了,但對于復雜一些類型如,如People的屬性Son(Son就是兒子,我一開始寫成了Sun),他也是一個People類型,他也有屬性的,而且他也可能有Son...

 先看下調用代碼吧:

1     People p4 = new People { Id = 1, Name = "鶴沖天", Brithday = new DateTime(199099) };
2     p4.Son = new People { Id = 2, Name = "鶴小天", Brithday = new DateTime(201599) };
3     p4.Son.Son = new People { Id = 3, Name = "鶴微天", Brithday = new DateTime(204099) };
4     string s4 = p4.ToString4("[Name] 的孫子 [Son.Son.Name] 的生日是:[Son.Son.Brithday: yyyy年MM月dd日]。");

 “鶴沖天”也就是我了,有個兒子叫“鶴小天”,“鶴小天”有個兒子,也就是我的孫子“鶴微天”。哈哈,祖孫三代名字都不錯吧(過會先把小天、微天這兩個名字注冊了)!主要看第4行,format是怎么寫的。下面是版本四實現代碼,由版本三改進而來:

 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擴展如下(版本三的實現用上這個擴展會更簡潔)(考慮性能請在此方法加緩存):

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     }

 先執行,再分析:

執行正確! 版本四,8~17行用來層層獲取屬性。也不太復雜,不多作解釋了。說明一下,版本四是不完善的,沒有做太多處理。

我們最后再來看一下更復雜的應用,Peoplee有Friends屬性,這是一個集合屬性,我們想獲取朋友的個數,并列出朋友的名字,如下:

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] 個朋友:[Friends: .Name]。");

 注意,行4中的Count及Name前都加了個小點,表示是將集合進行操作,這個小點是我看著方便自己定義的。再來看實現代碼,到版本五了:

 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<object> objs = ((IEnumerable)propertyValue).Cast<object>();
26                     if (subPropertyName == "Count")
27                         return objs.Count().ToString();
28                     else
29                     {
30                         string[] subProperties = objs.Select(
31                             o => o.GetPropertyValue(subPropertyName).ToString()).ToArray();
32                         return string.Join("", subProperties);
33                     }
34                 }
35                 else
36                     return string.Format("{0:" + propertyFormat + "}", propertyValue);
37             }
38             else return propertyValue.ToString();
39         };
40         string pattern = @"\[(?[^\[\]:]+)(\s*[:]\s*(?[^\[\]:]+))?\]";
41         return Regex.Replace(format, pattern, evaluator, RegexOptions.Compiled);
42     }

執行結果:

執行結果

 

比較不可思議吧,下面簡單分析一下。行22~行33是對集合進行操作的相關處理,這里只是簡單實現了Count,當然也可以實現Min、Max、Sum、Average等等。“.Name”這個表示方法不太好,這里主要是為了展示,大家能明白了就好。 

本文來自鶴沖天博客園文章《c#擴展方法奇思妙用高級篇五:ToString(string format) 擴展

【編輯推薦】

  1. DropDownList顯示的C#遞歸實現淺析
  2. C#treeview遞歸操作數據庫淺析
  3. C#遞歸樹實現實例簡析
  4. C#打開記事本實現實例解析
  5. C#調用記事本實例淺析
責任編輯:彭凡 來源: 博客園
相關推薦

2009-08-27 18:04:01

c#擴展方法string

2024-09-09 11:26:36

C#數字格式化

2009-07-31 14:03:21

C# Format函數

2012-03-09 10:02:29

Java

2009-08-10 17:36:17

C#擴展方法

2009-08-12 11:24:25

C# String對象

2009-09-01 11:19:47

C# 3.0擴展重載抉

2009-09-01 11:04:59

C#調用擴展方法

2009-08-31 14:45:10

C#擴展方法

2023-11-27 19:35:01

C++extern

2009-08-27 13:50:08

C# StringBu

2024-02-23 18:04:37

C++const關鍵字

2009-08-27 16:24:48

擴展方法C# 3.0新特性

2009-09-02 17:08:30

C#語言開發Windo

2009-08-28 14:25:57

C# byte數組

2009-08-26 15:53:48

C#擴展方法

2009-08-18 14:14:45

C#擴展方法性能測試

2009-08-27 09:27:49

C#擴展方法

2009-08-27 15:47:00

C#數據類型string

2009-08-31 14:56:32

C# Byte數組轉換
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产大片黄色 | aaaa一级毛片 | 精品国产免费一区二区三区演员表 | 亚洲一二三区在线观看 | 国产精品久久久久久久久久久久久久 | 久久激情视频 | av在线一区二区三区 | 中文字幕第三页 | 成年人网站在线观看视频 | 精品蜜桃一区二区三区 | 欧美日韩亚洲视频 | 欧美精品乱码久久久久久按摩 | 一级在线观看 | 成年人视频在线免费观看 | 日韩精品在线视频免费观看 | 操人网| 精品视频久久久久久 | www在线视频 | 亚洲男女视频在线观看 | 中文字幕不卡视频在线观看 | 91操操操 | 免费小视频在线观看 | 国产伊人精品 | 超碰成人免费观看 | 91久久国产综合久久 | 国产一区成人 | 亚洲天堂一区 | 成人夜晚看av | 亚洲福利网 | 久草精品视频 | 欧美寡妇偷汉性猛交 | 亚洲在线一区 | 国产精品欧美一区二区 | av免费网站在线观看 | 欧美日韩高清免费 | 欧美日本一区 | 色噜噜色综合 | 亚洲国产精品成人久久久 | 中文区中文字幕免费看 | 草久久| 99九色|