這些天太忙了都沒更新博客了,這篇我們繼續聊聊“屬性”,大家都知道,屬性其實分兩種,無參屬性和有參屬性,顧名思義
無參屬性就是我們平時用到的 “屬性”,有參屬性就是我們所說的 “索引器”,
1 public class Bird 2 { 3 public int Age { get; set; } 4 5 public string this[int i] { get { return i + string.Empty; } set { ;} } 6 }
乍一看這兩個還是蠻像的,本質上來說這兩個都是getXXX,setXXX方法,只是編譯器為了提高我們的開發效率而做的語法糖。
好,下面回答幾個小問題,當然是我自己的個人見解。
Q:為什么類型中要存在屬性?
A: 一般來說,一個類中都存在一個描述類的狀態數據,我們也可以認為是元數據,這些元數據是不可以被輕易修改的,一但
被錯誤的修改,就會導致類的破壞,所以建議在字段中加一層殼,由屬性來提供高層訪問。
舉個例子:Person的Age字段不能設為<0 || >150的非法數據,這個時候我們就可以在屬性的set方法上進行過濾了。
1 private int age; 2 3 public int Age 4 { 5 get 6 { 7 return age; 8 } 9 set 10 { 11 if (value < 0 || value > 150) 12 throw new Exception(); 13 age = value; 14 } 15 }
Q: 我看到上面字段age和屬性Age,那么請問自動屬性有封裝字段嗎,比如下面的代碼?
1 public int Age 2 { 3 get; 4 set; 5 }
A:其實這個問題問的好,如果你是平時用用而沒有用IL看一下的話,可能還真被蒙到了,既然說到了IL,那就用IL看一下。
從IL上可以清楚的看到其實編譯器給我們生成了一個私有的k__BackingField 字段。
Q: 提到屬性,我想問一下“類型初始化器”和“構造函數”有什么區別。
1 var b = new Bird { Name = "youyou", Age = 20 };
A: 要看有沒有區別,我們得要看到底這個“類型初始化器”到底干了些什么?老規矩,我們看看IL代碼。
從IL上可以看出,兩個nop之間,我們調用了構造函數(ctor),并且先后調用了set_Name,set_Age方法,所以本質上來說,
“類型初始化器”只是一個語法糖,跟我們手工在構造函數中初始化一樣。
Q:我經常看到Session["xxx"],Cookie["xxx"],請問索引器只能用到類的實例上嗎?可不可以
用到類型上?
A:這個問題問的好,其實你可以發現,我們在定義一個索引器的時候,根本就沒有定義索引器的名字,而是直接用this,重點
就在這里,我們知道this表示當前實例的上下文,導致我們的[]只能用到類型的實例上,也就做不了將[]用到類型上。
1 public string this[int i] 2 { 3 get { return i + string.Empty; } 4 set { ;} 5 }
Q:從上圖中看到索引器本質上是get_Item,set_Item,但是我如果自己手工定義了一個
get_Item造成方法名沖突了,這個怎么辦?
1 public class Bird 2 { 3 public string this[int i] 4 { 5 get { return i + string.Empty; } 6 set { ;} 7 } 8 9 //重名了,這個怎么辦? 10 public string get_Item(int s) 11 { 12 return string.Empty; 13 } 14 }
A: 這個問題也是蠻有意思的,最常見的做法就是手工修改我們自己定義的方法名,但是我們這里可不可以另辟蹊徑呢?我們在寫
WCF的時候,可能會遇到給方法標記別名的情況,然后我們就用OperationContract給方法換一個名字,現在估計就有人想到
了我是不是也可以給“索引器”加上別名?確實可以這樣,在這里我們可以用IndexerName來完成。
1 [IndexerName("Fly")] 2 public string this[int i] 3 { 4 get { return i + string.Empty; } 5 set { ;} 6 }
然后我們再看看IL代碼,就這樣成功的修改了索引器的方法名。
文章列表