C#系列文章之屬性。本站提示廣大學習愛好者:(C#系列文章之屬性)文章只能為提供參考,不一定能成為您想要的結果。以下是C#系列文章之屬性正文
屬性,允許源代碼用簡化語法來調用方法。CLR支持兩種屬性:無參屬性和有參屬性(索引器)。
1.1無參屬性特征
由於某些不恰當使用字段會破壞對象的狀態,所以一般會將所有字段都設為private。要允許用戶或類型獲取或設置狀態信息,需要提供封裝了字段訪問的方法(訪問器)。
public class Employee { private string name; public string GetName(){return name;} public string SetName(string value){name = value;} }上面進行數據封裝不得不實現額外的方法,類型用戶必須調用方法,不能直接引用字段名。為此CLR提供一個稱為屬性的機制。
public class Employee { private string name; public string Name
{ get{return name;} set{name=value;} } }
每個屬性都有名稱和類型(類型不能是void)。屬性不能重載。
C#編譯器發現獲取或設置屬性時,實際會生成如下代碼,在屬性名之前自動附加get_,set_前綴。
public class Employe { private string name; public string get_Name() { return n_Name; } pubic void set_Name() { n_Name=value; } }
針對源代碼中定義的每個屬性,編譯器會在托管程序集的元數據中生成一個屬性定義項,包含一些標志以及屬性類型。同時還引用了get和set訪問器方法。這些工作將屬性這種抽象概念與它的訪問器方法之間建立起一個聯系。
1.2 自動實現屬性
如果僅為了封裝一個支持字段而創建屬性,C#提供了稱為自動屬性的簡潔語法。public string Name{get;set;}C#會在內部自動聲明一個私有字段。
優點:訪問該屬性的任何代碼實際都會調用get和set方法。如果以後決定自己實現get和set方法,則訪問屬性的任何代碼都不必重新編譯。然而將Name聲明為字段,以後想改為屬性,那麼訪問字段的所有代碼都必須重新編譯才能訪問屬性方法。
缺點:AIP的支持字段名稱由編譯器決定,每次重新編譯都可能更改名稱。因此任何類型含有一個AIP,就沒辦法對該類型的實例進行反序列化。而運行時序列化引擎會將字段名持久存儲到序列化流中。任何想要序列化或反序列化的類型中都不要使用AIP功能。另外使用AIP不能加斷點,手動實現的屬性則是可以,方便調試。使用AIP屬性必然是可讀可寫的,只寫字段不能讀取值有什麼用?只讀肯定是有默認值,所以AIP作用於整個屬性的,不能顯式實現一個訪問器方法,而讓另一個自動實現。
1.3 屬性使用時需注意的點:
1.4 對象和集合初始化器
C#支持一種特殊的對象初始化語法 Employee e = new Employee(){Name="abc",Age = 45}。這句話將構造一個對象,調用它的無參構造函數,將公共Name屬性設為"abc"等,等同於e.Name = "abc";e.Age = 45;兩種方式生成的IL代碼相同。同時C#還允許組合多個函數,比如
Employee e = new Employee(){Name="abc",Age = 45}.ToString().ToUpper();
如果屬性類型實現了IEnumerable或IEnumerable<T>接口,屬性就被認為是集合,而集合的初始化是一種相加操作。如下:
public class ClassRoom { private List<string> students = new List<string>(); public List<string> Students{get{return students;}} //初始化,實際上是調用集合自帶的Add方法,將數據添加到集合中 ClassRoom classRoom = new ClassRoom{Students = {"sd","sdf","fgd"};}
}
//對於字典類型初始化
var table = new Dictionary<string,int>{ {"abc",1}, {"def",2} }
1.5 匿名類型
匿名類型的功能可以自動聲明不可變的元組類型。元組類型含有一組屬性的類型。var o1 = new {Name="abc",Year = 1990}
編譯器會推斷每個表達式的類型,創建推斷類型的私有字段,為每個字段創建公共只讀屬性,並創建一個構造器來接受所有表達式,在其中會用傳給他的表達式求值結果來初始化私有字段。還會重寫Object的Equals,GetHashCode和ToString方法,生成所有這些方法的代碼。
如果在源代碼中定義了多個匿名類型,而且這些類型具有相同的結構(每個屬性都有相同的類型和名稱,而且這些屬性的指定順序相同),那麼它只會創建一個匿名類型定義,並創建該類型多個實例。
一般匿名類型經常和LINQ配合使用。方法原型不能接受匿名類型參數,因為不知道是什麼類型,也不能返回對匿名類型的引用,想要傳遞元組,可以使用System.Tuple類型。
string myDocuments = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); var query = from pathname in Directory.GetFiles(myDocuments) let LastWriteTime = File.GetLastWriteTime(pathname) where LastWriteTime>(DateTime.Now - TimeSpan.FromDays(7)) orderby LastWriteTime select new {Path = pathname,LastWriteTime};
1.6 有參屬性
C#使用數組風格的語法公開有參屬性(索引器),可將索引器是C#開發人員對[]操作符的重載。比如定義一個類Student
public class Student { public string this[int n] { get { return Name; } set { Name = n > 0 ? "大於0" : "小於0"; } } private string Name; }
當執行Student stu = new Student();stu[4] = "NNN";時執行set方法,將Name設為大於0;執行stu[4]時輸出大於0字樣。
1.7 調用屬性訪問器方法時的性能
對於簡單的get和set訪問器方法,JIT編譯器會將代碼內聯(將方法也就是訪問器方法的代碼直接編譯到調用它的方法中),避免在運行時發出調用所產生的開銷,代價是編譯好的方法變得更大。注意:JIT在調試代碼時不會內聯屬性方法,因為這將難以調試,發行版本會使用內聯,性能會快。
1.8 既然屬性本質上是方法,而C#和CLR支持泛型方法,但是C#不允許屬性引入自己的泛型類型參數,最主要原因屬性本來應該表示可供查詢或設置某個對象特征。一旦引入泛型類型參數就意味可能改變查詢/設置行為,但屬性不應該和行為沾邊,公開對象的行為無論是不是泛型,都應該定義方法而非屬性。