[讀書筆記]C#學習筆記五: C#3.0自動屬性,匿名屬性及擴展方法


[讀書筆記]C#學習筆記五: C#3.0自動屬性,匿名屬性及擴展方法

前言html

這一章算是看這本書最大的收穫了, Lambda表達式讓人用着屢試不爽, C#3.0可謂顛覆了咱們的代碼編寫風格. 由於Lambda所需篇幅挺大, 因此先總結C#3.0智能編譯器給咱們帶來的諸多好處, 下一遍會單獨介紹Lambda表達式. 這篇主要包括的內容有: 自動屬性,隱式類型,對象集合初始化,匿名類型,擴展方法.數組

下面一塊兒來看下C#3.0 所帶來的變化吧.微信


1,自動實現的屬性
在C#3.0以前, 定義屬性時通常會像下面這樣去編寫代碼:ide

複製代碼

 1 class Person 2 { 3     //定義私有字段 4     private string name; 5  6     //定義可讀寫屬性 7     public string Name 8     { 9         get{return name;}10         set{name = value;}11     }12 13     private int age;14     //定義只讀屬性15     public int Age16     {17         get{return age;}18     }19 }

複製代碼

 

PS: 這裏有一個快捷鍵: private string name; 選中name 而後Ctrl + R + E 就能夠快捷生成Name屬性.函數

C#3.0以後, 對於不須要額外驗證的屬性(須要額外驗證的屬性仍是必須採用以前的方式來定義), 咱們可使用自動實現的特性來對屬性的定義進行簡化, 此時再也不需額外定義一個私有字段了.代碼以下:工具

複製代碼

1 class Person2 {3     //使用自動實現的屬性來定義屬性4     //定義可讀寫屬性5     public string Name{get; set;}6     //定義只讀屬性7     public int Age{get; private set;}8 }

複製代碼


PS: 這裏也有一個快捷鍵: 打出prop 而後點擊兩下Tab鍵就能夠生成上面的屬性了, 不過還需手動改值.
相似的快捷鍵有: 輸入cw 而後點擊兩下Tab鍵 就能夠直接生成Console.WriteLine();了. 相似的還有不少, 在這裏就不列舉了.post

之因此能夠這樣定義屬性, 主要是由於編譯器在編譯時會爲咱們建立一個私有字段. 利用反編譯工具能夠知道使用自動實現的屬性時,C#都會幫咱們建立必要的字段.
另外在結構體中使用自動屬性時, 須要注意的是全部構造函數都須要顯式地調用無參構造函數this, 不然會出現編譯錯誤. 由於只有這樣,編譯器才能知道全部的字段都已經被賦值.
然而, 類卻不須要顯式地調用無參構造函數, 這主要是由C#編譯器的設計決定, 咱們記住就行了.學習

複製代碼

 1 public struct TestPerson 2 { 3     public string Name { get; set; } 4     public int Age { get; private set; } 5  6     //結構體中, 不顯式地調用無參構造函數this()時, 會出現編譯時錯誤 7     public TestPerson(string name) 8     : this() 9     {10         this.Name = name;11     }12 }

複製代碼

 

2,隱式類型 var關鍵字
C#是強類型語言, 在定義一個變量時, 須要聲明變量的類型. 然而類型的長度過長,就可能影響代碼的可讀寫性. 爲了不這樣的問題, C#3.0 引入了隱式類型,便可以使用關鍵字var來聲明變量或數組.
var關鍵字告訴編譯器去根據變量的值來推斷其類型.this

複製代碼

1 class Program2 {3     static void Main(stirng[] args)4     {5         //用var聲明局部變量6         var stringValue = "Barry Wang";7         stringValue = 2;8     }9 }

複製代碼

 

這裏就會報錯"沒法將類型int 隱式轉換爲string". 調試會發現stringValue是string類型的.
使用隱式類型有一些限制, 包括如下幾點:
(1)被聲明的變量是一個局部變量, 不能爲字段
(2)變量在聲明時必須被初始化, 由於編譯器要根據變量的賦值來推斷類型
(3)變量不能初始化爲一個方法組, 也不能爲一個匿名函數
url

 

3,對象集合初始化
在C#3.0以前定義類, 咱們每每須要定義多個構造函數來完成不一樣狀況下的初始化, C#3.0 提供了對象初始化器, 它減小了咱們在類中定義的構造函數代碼, 從而使代碼更加簡潔.

複製代碼

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5         //沒有對象初始化器時的代碼 6         Person p = new Person("BarryWang", 25); 7         p.Weight = 60; 8         p.Height = 170; 9     }10 }11 12 public class Person13 {14     public string Name { get; set; }15     public int Age { get; set; }16     public int Weight { get; set; }17     public int Height { get; set; }18 19     //定義不一樣狀況下的初始化函數20     public Person() { }21     public Person(string name) { } 
22     public Person(string name, int age) { }23     public Person(string name, int age, int weight) { }24     public Person(string name, int age, int weight, int height) 
25     {26         this.Name = name;27         this.Age = age;28         this.Weight = weight;29         this.Height = height;30     }31 }

複製代碼

 

在C#3.0引入對象初始化以後, 咱們就不須要定義多個構造函數了, 前面的代碼能夠簡化爲以下的形式:

複製代碼

 1 class Program 2 { 3     static void Main(string[] args) 4     { 5         //使用對象初始化器初始化 6         Person p = new Person() {Name = "Barry Wang", Age = 25, Weght = 60, Height = 70}; 7         //下面這行代碼和上面一行是等價的, 只不過下面省略了構造函數的圓括號而已 8         Person p2 = new Person {Name = "Barry Wang", Age = 25, Weght = 60, Height = 70}; 9     }10 }11 12 public class Person13 {14     public string Name { get; set; }15     public int Age { get; set; }16     public int Weight { get; set; }17     public int Height { get; set; }18 }

複製代碼

 

利用反編譯工具能夠知道編譯器爲對象作了以下處理: 首先C#編譯器生成了一個Person類的臨時對象, 並調用Person類的默認無參構造函數對其初始化.而後對它的屬性逐個賦值.
由此能夠想到,要使用對象初始化器,則必須保證類中具備一個無參構造函數. 若是咱們自定義了一個有參構造函數而把默認的無參構造函數覆蓋了, 則須要從新定義一個無參構造函數.

再例如 給List 中添加元素, 在C#3.0 以前咱們須要一個個Add 添加, 而如今直接能夠利用集合初始化器便可, 編譯器會調用Add方法, 一個個地將初始化的內容添加進去.

複製代碼

1 class Program2 {3     List<string> newNames = new List<string>4     {5         "A", "B", "C"6     };7 }

複製代碼


4,匿名類型
匿名類型顧名思義就是沒有知名類型的類型, 經過隱式類型和對象初始化器兩種特性建立了一個類型未知的對象, 使咱們在不定義類型的狀況下能夠實現對象的建立,從而減小了類定義過程的代碼.

複製代碼

 1 class Program 2 { 3     static void Main(string[] args) 4     { 
 5         //定義匿名類型對象 6         var person = new {Name = "Barry Wang", Age = 25 }; 7         Console.WriteLine("{0} 的年齡爲: {1}", person.Name, person.Age); 8  9         //定義匿名類型數組10         var personCollection = new[]11         {12             new {Name = "Barry", Age = 30},13             new {Name = "Brad", Age = 22},14             new {Name = "Tony", Age = 25}15         };16 17         int totalAge = 0;18         foreach (var p in personCollection)19         {20             //下面一行代碼證實了Age屬性時int類型21             totalAge += p.Age;22         }23 24         Console.WriteLine("全部人的年齡總和爲: {0}", totalAge);25         Console.ReadKey();26     }27 }

複製代碼

 

 

5,擴展方法
擴展方法, 首先是一個方法, 他能夠用來擴展已定義類型中的方法成員.
以下例所示:

複製代碼

 1 public static class Extensions 2 { 3     //對string 類擴展,注意this關鍵字 4     public static void TestStr(this string s) 5     { 6         Console.WriteLine(s.ToString()); 7     } 8     //對int 類擴展 9     public static void TestInt(this int i, string s)10     {11         Console.WriteLine(s + i.ToString());12     }13 }14 class Program15 {16     static void Main(string[] args)17     {18         //使用擴展方法19         string s = "Test Extensions Methods";20         s.TestStr();//調用方法121         Extensions.TestStr(s);//調用方法222         int i = 100;23         i.TestInt("This value is ");24         Extensions.TestInt(i, "This value is ");25         Console.ReadKey();26     }27 }

複製代碼

 

並非全部的方法均可以用做擴展方法, 咱們須要考察它是否符合下列擴展方法的定義規則:
(1)擴展方法必須在一個非嵌套, 非泛型的靜態類中定義
(2)它至少要有一個參數
(3)第一個參數必須加上this關鍵字做爲前綴(第一個參數類型也稱爲擴展類型, 即指方法對這個類型進行擴展)
(4)第一個參數不能使用任何其餘修飾符(如不能使用ref out等)

複製代碼

 1 namespace CurrentNameSpace 2 { 3     //要想使用不用命名空間下的擴展方法, 須要先引入該命名空間 4     using CustomNameSpace; 5     class Program 6     { 7         static void Main(string[] args) 8         { 9             Person p = new Person {Name = "BarryWang"};10             p.Print();//等價於==>ExtensionClass.Print(p);11             p.Print("Hello");12             Console.ReadKey();13         }14     }15 16     //自定義類型17     public class Person18     {19         public string Name { get; set; }20     }21 22     //當前命名空間下的擴展方法定義23     public static class ExtensionClass24     {25         public static void Print(this Person p)26         {27             Console.WriteLine("調用的是當前命名空間下的擴展方法輸出, 姓名爲: {0}", p.Name);28         }29     }30 }31 32 namespace CustomNameSpace33 {34     using CurrentNameSpace;35     public static class CustomExtensionClass36     {37         //擴展方法定義38         public static void Pint(this Person p)39         {40             Console.WriteLine("調用的是CustomNameSpace命名空間下擴展方法輸出: 姓名爲: {0}", p.Name);41         }42 43         //擴展方法定義44         public static void Pint(this Person p, string s)45         {46             Console.WriteLine("調用的是CustomNameSpace命名空間下擴展方法輸出: 姓名爲: {0},附加字符串爲{1}", p.Name, s);47         }48     }49 }

複製代碼

 

打印結果是:
調用的是當前命名空間下的擴展方法輸出, 姓名爲: Barry Wang
調用的是CustomNameSpace命名空間下擴展方法輸出, 姓名爲: Barry Wang, 附加字符串爲Hello

註解: 你們看到p.Print(); 是否有些疑惑呢? 對於C#3.0編譯器而言, 當它看到某個類型的變量在調用方法時, 它會首先去該對象的實例方法進行chazhao,若是沒有找到與調用方法同名並參數一致的實例方法,
編譯器就回去查找是否存在合適的擴展方法. 編譯器會檢查全部導入的命名空間和當前命名空間中的擴展方法, 並將變量類型匹配到擴展類型.
從編譯器發現擴展方法的過程來看, 方法調用的優先級別順序爲: 類型的實例方法-->當前命名空間下的擴展方法-->導入命名空間的擴展方法.

解釋上面代碼打印結果的由來: 在以上代碼中存在另個不一樣的命名空間, 它們都定義了帶一個參數的擴展方法Print. 根據執行順序, 編譯器會首先查看Person類型中是否認義了無參的Print實例方法.
若是有, 則中止查找; 不然繼續查找當前命名空間下, 即CurrentNameSpace下是否認義了一個帶參數的擴展方法Print.
此時在CurrentNameSpace命名空間下, 正好存在帶一個擴展類型參數的Print擴展方法, 編譯器所以不會再繼續查找其餘引入命名空間了. 因此p.Print() 調用的是當前命名空間下的Print擴展方法.
而p.Print("Hello")的調用也是一個道理.

 

PS: 終於把今天的兩篇博文更新完了, 這麼冷的天 真是不容易, 記錄到這裏但願本身之後有時間還會再看看吧!  

分類: C#基礎系列讀書筆記

好文要頂 關注我 收藏該文  

一枝花算不算浪漫

相關文章
相關標籤/搜索