CLR类型设计之属性
? ? ? ? ? 在之前的隨筆中,我們探討了參數,字段,方法,我們在開始屬性之前回顧一下,之前的探討實際上串聯起來就是OOP編程的思想,在接下來的文章中,我們還會討論接口(就是行為),舉個例子:我們如果要做一個學生檔案,我們需要先抽象出來有那些對象實體,比如有一個學生類,里面有學生id,姓名,年齡,班級等字段。?但是這不能滿足我們的需求,我們要做學生檔案管理,需要知道學生的每科成績,所以我們還需要一個成績類,里面定義了學生的學生Id,科目,科目分數,下面是兩個類的代碼示例
public sealed class Student{//學員idpublic int StudentId;//姓名public string Name;//年齡public int Age;//班級名public string classname;}public sealed class Score {//學員idpublic int StudentId;//科目public string SubjectName;//成績public int Achievement;} View Code? ? ? ?有了這兩個類以后,我們就可以創建一個獲取學員成績的方法,方法的代碼示例就不寫了,OOP的思想最重要的在于盡可能的模塊化可復用,當然為了實現這些,還有繼承,多態等去實現目的,但是在繼續實現過程中你可能會發現一些問題,當我們需要使用上面類型的時候,可以通過實例化直接使用,如下:
Student stu = new Student();stu.Name = "蘇云";stu.Age = 22;stu.classname = "fantasy"; View Code? ? ? 但是如果我輸入一個Age為-15,程序也可以通過,字段值就會被改變為-15,年齡是不存在負數的,所以這個值是不應該通過的,這就是今天的主題,屬性設置,面向對象一條很重要的原則就是數據封裝,意味類型字段永遠不應該公開,否則很容易因為不恰當使用而破壞對象的狀態,如上文我們輸入的負值,當然還有一些其他原因,比如線程安全,字段為邏輯字段,其值存在于內存中的字節,通過某個算法獲取得到。但是這樣做就會導致一個問題,外部方法想要訪問時,由于內部不公開,所以外部無法訪問
? ? ? CLR中提供了屬性機制,我們完全可以不用擔心上面的問題,我們改寫一下例子一的代碼示例,在實例化學生的時候,如果Age<1,就會拋出異常,可以看到是那個參數報出的異常,值是多少。
public sealed class Student{private int studentId;private string name;private int age;private string classname;//學員idpublic int StudentId { get { return (studentId); }set { studentId = value; } }//姓名public string Name{ get { return (name); }set { name = value; } }//年齡public int Age{get { return (age); }set{if (value<1) throw new ArgumentOutOfRangeException("value", value.ToString(),"學生的年齡不能小于1歲");age = value;}}//班級名public string Classname { get { return (classname); }set { classname = value; } }} View Code? ? 屬性機制使用起來很簡單,每個屬性都有名稱和類型(類型不能為void),并且一個類中同一個字段名稱只能出現一次,只需要get,set兩個關鍵字,如果只有get那就是只讀字段,只有set是只寫字段。也可以在get上寫計算方法獲取到值,但是上述方法寫起來是否覺得很麻煩?C#還支持自動屬性實現,我們改寫成績類,示例代碼如下,
public sealed class Score {//學員idpublic int StudentId { get; set; }//科目public string SubjectName { get; set; }//成績public int Achievement { get; set; }} View Code? ?在C#中聲明get;set但是卻未提供對應方法,C#會自動聲明一個私有字段,這樣就可以很快定義一個屬性,和寫字段是一樣的,但需要注意的是,屬性不能作為out或ref參數傳給方法,而字段可以
? ? ?對象和初始化器
? ? ? 在之前的代碼中,我們初始化學生類需要分兩步,第一實例化,第二賦值,但實際上我們可以使用更簡單的語法,對象初始化器初始化一個對象,只需要像下面這樣一句話就可以初始化一個對象并且賦值,他做的事情和例子2是相同的。在集合中也可以使用初始化器初始化集合。
?
重點: 如果類沒有無參的構造函數就會出現編譯時錯誤?
Student stu1 = new Student() {Name="admain",Age=15,Classname="fantasy"}; View Code? ? 我們提到集合也可以用初始化器的方法初始化,但是集合的初始化和對象并不一樣,首先要求對象或字段繼承了IEnummerable<T>接口,我們示例常見的Dictionary集合如何初始化
1 Dictionary<int, string> dic = new Dictionary<int, string> { 2 { 1,"張三"}, { 2,"李四"} 3 }; 4 //等價于 5 dic.Add(1, "張三"); 6 dic.Add(1, "李四"); View Code? ? ?有參屬性:索引器
? ? ?一個屬性的get訪問器方法不接收參數,則稱為無參屬性,用起來就和訪問字段一樣,除了這些與字段相似的屬性,還有一種有參屬性,C#里稱其為索引器,下文中所有有參屬性都用索引器替代,C#使用數組風格的語法來公開索引器,看下面的示例:
1 class MyListBox 2 { 3 public ArrayList data = new ArrayList(); 4 public object this[int idx] //this作索引器名稱,idx是索引參數 5 { 6 get 7 { 8 if (idx > -1 && idx < data.Count) 9 { 10 return data[idx]; 11 } 12 else 13 { 14 return null; 15 } 16 } 17 set 18 { 19 if (idx > -1 && idx < data.Count) 20 { 21 data[idx] = value; 22 } 23 else if (idx <= data.Count) 24 { 25 data.Add(value); 26 } 27 else 28 { 29 throw new ArgumentOutOfRangeException("idx", idx, "超出數組索引范圍"); 30 } 31 } 32 } 33 } View Code? ? ? 我們定義了一個類MyListBox,其中有一個ArrayList字段,在構造器中為其默認初始化了,在下面的代碼中我們看到了如何聲明一個索引器,我們返回的類型是object,索引器的返回類型一樣不可以void,c#使用this[...]表達索引器,并且C#不支持靜態索引器,盡管CLR支持靜態有參屬性,C#允許一個類型定義多個索引器,但是索引器參數集不能相同,其他一些語言中支持定義多個相同簽名的索引器,因為其他一些語言中索引器可以自定命名,但是C#不允許這樣做,因為C#中不允許開發人員指定索引器名稱,C#為一個類型中的所有索引器都默認提供了一個叫做Item的名稱,所以在C#中使用索引器只能通過不同簽名來區分選擇的索引器。
? ? ? ?CLR并不區分有參屬性和無參屬性,對CLR來說,每個屬性都只是類型中定義的一對方法,和一些元數據。下面的示例是如何調用索引器。使用起來也很簡單吧
1 //初始化MyListBox 2 MyListBox ba = new MyListBox { 3 //集合初始化器初始化值 4 data = { "張三",20,30,40}, 5 }; 6 //調用添加方法為其添加值 7 ba.data.Add("5"); 8 ba.data.Add(6); 9 for (int i = 0; i < ba.data.Count; i++) 10 { 11 //使用索引器打印出指定值,具體實現請查看類中get方法 12 Console.WriteLine(ba.data[i]); 13 } View Code? ? ? 無參屬性,初始化器,有參屬性,有了這些你可以在你的方法中更好的使用字段,并且讓你的數據封裝更加安全,但是CLR作者本人卻持有另外一種觀點,作者覺得屬性不如封裝的方法。有興趣的朋友可以自己翻閱CLR看看作者的觀點。
?
轉載于:https://www.cnblogs.com/Demon-Su/p/7258436.html
總結
以上是生活随笔為你收集整理的CLR类型设计之属性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android使用ImageView显示
- 下一篇: 第一个WindowService服务