大家來討論下C#無參屬性這個東西到底有沒有存在的必要?
永遠不要公開類型的欄位,物件導向設計和編程的重要原則之一就是 資料封裝。如果公開類型的欄位,會很容易的寫出不恰當使用欄位的代碼。這裡可以把欄位聲明為private,然後聲明訪問器方法。
class test
{
private string Name;
public void SetName(string value)
{
Name = value;
}
public string GetName()
{
return Name;
}
}
實現唯讀或唯寫就是這麼簡單,不實現一個索引器方法就行了。還可以將Setname方法標記為protected,就可以只允許衍生類別修改了。聰明的你也發現上面這個做法的缺點了吧,首先得去實現額外的方法,所以會產生更多的代碼,其次,如果調用的時候必須調用方法,不能直接引用一個欄位名。還好C#給我們提供了一個稱為屬性(property)的機制,它緩解了第一個缺點造成的影響,也消除了第二個缺點。
class test
{
private string name;
public string Name {
get { return name; }
set { if ("狗剩" == value) { Console.WriteLine("系統崩潰,拋出異常"); return; } name = value; }
}
}
屬性可以用任意的可存取修飾詞來標記。不能定義名稱相同的屬性。
如果只是為了封裝一個支援欄位而建立屬性,C#還提供一一種更簡單的文法,稱為自動實作屬性(Automatically Implemented Property,簡稱AIP)。like this
class test
{
public int Age
{
get;
set;
}
}
如果聲明一個屬性而不提供get/set實現,C#會自動為你聲明一個私人欄位。原始代碼是這樣的:
public int Age
{
[CompilerGenerated]
get
{
int num;
num = this.<Age>k__BackingField;
Label_0009:
return num;
}
[CompilerGenerated]
set
{
this.<Age>k__BackingField = value;
return;
}
}
和直接聲明一個public 的Age欄位相比,自動實作屬性的優勢在哪?兩者存在一處重要的區別:使用自動實作屬性(AIP)意味著已經建立一個屬性,訪問該屬性的時候會調用get/set方法。如果有一天你決定自己實現這個AIP的get/set,而不接受編譯器預設的實現,那麼訪問這個屬性的任何代碼都不必重新編譯。如果是一個public的Age欄位,如果你把它更改為屬性,那麼訪問這個欄位的所有代碼就都需要重新編譯了。
凡事都有好有壞,那麼AIP有哪些不討人喜歡的地方呢:
1.如果使用欄位聲明的文法,可以包含初始化的部分,如果使用AIP就不行了,必須在咩個構造器中顯示的初始化每個AIP。
2.AIP支援的欄位名是由編譯器決定的,而且每次重新編譯,這個欄位名可能會發生更改,這樣一來,只要含有一個AIP就不能對類型的執行個體進行還原序列化了。在任何想要序列化或還原序列化的類型中,都不要使用AIP功能。
3.不能在AIP的get/set添加斷點,所以不好檢測程式在什麼時候擷取或設定這個屬性。咱手動實現的就可以設定斷點,不信你試試。
AIP功能是作用於整個屬性的:要麼都用,要麼都不用,這意味這,如果顯式的實現get那麼set也要顯式實現,反之亦然。
屬性看起來與欄位相似,但本質上是方法。屬性與欄位的區別如下:
1.屬性可以是唯讀或唯寫的(get/set訪問器方法),欄位卻總是可讀可寫的。
2.屬性方法可能拋出異常,欄位訪問不會拋出。
3.屬性不能作為out或ref參數傳遞給方法,欄位可以。
class Program
{
public int Age
{
get {return 3 ;}
set{}
}
int age;
static void Main()
{
var t = new { name="XiaoBai",age=22 };
test tt = new test();
//tt.Name = "狗剩";
Program p = new Program();
some(out p.age);
some(out p.Age);//屬性,索引器或動態成員不得作為out或ref參數傳遞
Console.ReadKey();
}
static void some(out int age)
{
age = 10;
}
}
4.屬性方法可能花費較長時間執行,欄位的訪問都是立即完成。
5.屬性方法可能需要額外的記憶體,或者返回一個不正確的引用,指向不屬於對象狀態一部分的某個東西,這樣一來,對返回對象的修改就作用不到原始對象身上了,相反查詢欄位返回的總是正確的引用,它指向的東西保證是原始對象狀態的一部分。使用會返回一個拷貝的屬性時,非常容易引起混淆。
如果仔細研究下屬性和欄位的差別,你會發現只有在極個別的情況下屬性才真正有用。屬性唯一的好處就是提供了簡化的文法,和調用普通方法(非屬性中的方法)相比,屬性不僅不會提升代碼的效能,還會妨礙對代碼的理解。
屬性訪問器的可訪問性:
我們有時希望為get訪問器指定一種可訪問性,為set訪問器指定另一種訪問器:
class test
{
public int Age
{
get { return age; }
protected set { if (value < 0) Console.WriteLine("年齡錯誤!"); else { age = value; } }
}
int age;
}
如上所述,Age屬性本身聲明為public,意味這get訪問器方法是公用的,可由所有代碼訪問,但是,注意set訪問器方法被聲明為protected,只能從test的內部定義的代碼中調用,或者從test的衍生類別的代碼中調用。
定義一個屬性時,如果兩個訪問其方法需要具有不同的可訪問性,C#文法要求必須為屬性本身指定限制最不大的那一種可訪問性,然後在兩個訪問其中,只能選擇一個來應用限制較大的那一種可訪問性。在前面的例子中,屬性本身為public,set訪問器為protected(限制比public大)。
class test
{
private int Age
{
get { return age; }
protected set { if (value < 0) Console.WriteLine("年齡錯誤!"); else { age = value; } }//這裡就通不過編譯
}
int age;
}