国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發(fā) > 綜合 > 正文

C#銳利體驗之第七講 域與屬性

2024-07-21 02:29:45
字體:
供稿:網(wǎng)友
,歡迎訪問網(wǎng)頁設(shè)計愛好者web開發(fā)。

域(field)又稱成員變量(member variable),它表示存儲位置,是c#中類不可缺少的一部分。域的類型可以是c#中任何數(shù)據(jù)類型。但對于除去string類型的其他引用類型由于在初始化時涉及到一些類的構(gòu)造器的操作,我們這里將不提及,我們把這一部分內(nèi)容作為“類的嵌套”放在“接口 繼承與多態(tài)”一講內(nèi)來闡述。

域分為實例域和靜態(tài)域。實例域?qū)儆诰唧w的對象,為特定的對象所專有。靜態(tài)域?qū)儆陬悾瑸樗袑ο笏灿谩#嚴格規(guī)定實例域只能通過對象來獲取,靜態(tài)域只能通過類來獲取。例如我們有一個類型為myclass的對象myobject,myclass內(nèi)的實例域instancefield(存取限制為public)只能這樣獲取:myobject. instancefield。而myclass的靜態(tài)域staticfield(存取限制為public)只能這樣獲取:myclass.staticfield。注意靜態(tài)域不能像傳統(tǒng)c++那樣通過對象獲取,也就是說myobject.staticfield的用法是錯誤的,不能通過編譯器編譯。

域的存取限制集中體現(xiàn)了面向?qū)ο缶幊痰姆庋b原則。如前所述,c#中的存取限制修飾符有5種,這5種對域都適用。c#只是用internal擴展了c++原來的friend修飾符。在有必要使兩個類的某些域互相可見時,我們將這些類的域聲明為internal,然后將它們放在一個組合體內(nèi)編譯即可。如果需要對它們的繼承子類也可見的話,聲明為protected internal即可。實際上這也是組合體的本來意思--將邏輯相關(guān)的類組合封裝在一起。

c#引入了readonly修飾符來表示只讀域,const來表示不變常量。顧名思義對只讀域不能進行寫操作,不變常量不能被修改,這兩者到底有什么區(qū)別呢?只讀域只能在初始化--聲明初始化或構(gòu)造器初始化--的過程中賦值,其他地方不能進行對只讀域的賦值操作,否則編譯器會報錯。只讀域可以是實例域也可以是靜態(tài)域。只讀域的類型可以是c#語言的任何類型。但const修飾的常量必須在聲明的同時賦值,而且要求編譯器能夠在編譯時期計算出這個確定的值。const修飾的常量為靜態(tài)變量,不能夠為對象所獲取。const修飾的值的類型也有限制,它只能為下列類型之一(或能夠轉(zhuǎn)換為下列類型的):sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, enum類型, 或引用類型。值得注意的是這里的引用類型,由于除去string類型外,所有的類型出去null值以外在編譯時期都不能由編譯器計算出他們的確切的值,所以我們能夠聲明為const的引用類型只能為string或值為null的其他引用類型。顯然當我們聲明一個null的常量時,我們已經(jīng)失去了聲明的意義--這也可以說是c#設(shè)計的尷尬之處!

這就是說,當我們需要一個const的常量時,但它的類型又限制了它不能在編譯時期被計算出確定的值來,我們可采取將之聲明為static readonly來解決。但兩者之間還是有一點細微的差別的。看下面的兩個不同的文件:

//file1.cs//csc /t:library file1.csusing system;namespace mynamespace1{public class myclass1{public static readonly int myfield = 10;}}//file2.cs//csc /r:file1.dll file2.csusing system;namespace mynamespace2{public class myclass1{public static void main(){console.writeline(mynamespace1.myclass1.myfield);}}}
我們的兩個類分屬于兩個文件file1.cs 和file2.cs,并分開編譯。在文件file1.cs內(nèi)的域myfield聲明為static readonly時,如果我們由于某種需要改變了myfield的值為20,我們只需重新編譯文件file1.cs為file1.dll,在執(zhí)行file2.exe時我們會得到20。但如果我們將static readonly改變?yōu)閏onst后,再改變myfield的初始化值時,我們必須重新編譯所有引用到file1.dll的文件,否則我們引用的mynamespace1.myclass1.myfield將不會如我們所愿而改變。這在大的系統(tǒng)開發(fā)過程中尤其需要注意。實際上,如果我們能夠理解const修飾的常量是在編譯時便被計算出確定的值,并代換到引用該常量的每一個地方,而readonly時在運行時才確定的量--只是在初始化后我們不希望它的值再改變,我們便能理解c#設(shè)計者們的良苦用心,我們才能徹底把握const和readonly的行為!

域的初始化是面向?qū)ο缶幊讨幸粋€需要特別注意的問題。c#編譯器缺省將每一個域初始化為它的默認值。簡單的說,數(shù)值類型(枚舉類型)的默認值為0或0.0。字符類型的默認值為'/x0000'。布爾類型的默認值為false。引用類型的默認值為null。結(jié)構(gòu)類型的默認值為其內(nèi)的所有類型都取其相應的默認值。雖然c#編譯器為每個類型都設(shè)置了默認類型,但作為面向?qū)ο蟮脑O(shè)計原則,我們還是需要對變量進行正確的初始化。實際上這也是c#推薦的做法,沒有對域進行初始化會導致編譯器發(fā)出警告信息。c#中對域進行初始化有兩個地方--聲明的同時進行初始化和在構(gòu)造器內(nèi)進行初始化。如前所述,域的聲明初始化實際上被編譯器作為賦值語句放在了構(gòu)造器的內(nèi)部的最開始處執(zhí)行。實例變量初始化會被放在實例構(gòu)造器內(nèi),靜態(tài)變量初始化會被放在靜態(tài)構(gòu)造器內(nèi)。如果我們聲明了一個靜態(tài)的變量并同時對之進行了初始化,那么編譯器將為我們構(gòu)造出一個靜態(tài)構(gòu)造器來把這個初始化語句變成賦值語句放在里面。而作為const修飾的常量域,從嚴格意義上講不能算作初始化語句,我們可以將它看作類似于c++中的宏代換。

屬性

屬性可以說是c#語言的一個創(chuàng)新。當然你也可以說不是。不是的原因是它背后的實現(xiàn)實際上還是兩個函數(shù)--一個賦值函數(shù)(get),一個取值函數(shù)(set),這從它生成的中間語言代碼可以清晰地看到。是的原因是它的的確確在語言層面實現(xiàn)了面向?qū)ο缶幊桃恢币詠韺Α皩傩浴边@一oo風格的類的特殊接口的訴求。理解屬性的設(shè)計初衷是我們用好屬性這一工具的根本。c#不提倡將域的保護級別設(shè)為public而使用戶在類外任意操作--那樣太不oo,或者具體點說太不安全!對所有有必要在類外可見的域,c#推薦采用屬性來表達。屬性不表示存儲位置,這是屬性和域的根本性的區(qū)別。下面是一個典型的屬性設(shè)計:

using system;class myclass{int integer;public int integer{get {return integer;}set {integer=value;}}}class test{public static void main(){myclass myobject=new myclass();console.write(myobject.integer);myobject.integer++;console.write(myobject.integer);}}
一如我們期待的那樣,程序輸出0 1。我們可以看到屬性通過對方法的包裝向程序員提供了一個友好的域成員的存取界面。這里的value是c#的關(guān)鍵字,是我們進行屬性操作時的set的隱含參數(shù),也就是我們在執(zhí)行屬性寫操作時的右值。

屬性提供了只讀(get),只寫(set),讀寫(get和 set)三種接口操作。對域的這三種操作,我們必須在同一個屬性名下聲明,而不可以將它們分離,看下面的實現(xiàn):

class myclass{private string name;public string name {           get { return name; }}public string name {          set { name = value; }}}
上面這種分離name屬性實現(xiàn)的方法是錯誤的!我們應該像前面的例子一樣將他們放在一起。值得注意的是三種屬性(只讀,只寫,讀寫)被c#認為是同一個屬性名,看下面的例子:

class myclass{protected int num=0;public int num{set {num=value;}}}class myclassderived: myclass{new public int num{get {return num;}}}class test{public static void main(){myclassderived myobject = new myclassderived();//myobject.num= 1;  //錯誤 ! ((myclass)myobject).num = 1;   }}
我們可以看到myclassderived中的屬性num-get{}屏蔽了myclass中屬性num-set{}的定義。

當然屬性遠遠不止僅僅限于域的接口操作,屬性的本質(zhì)還是方法,我們可以根據(jù)程序邏輯在屬性的提取或賦值時進行某些檢查,警告等額外操作,看下面的例子:

class myclass{private string name;public string name {           get { return name; }set {if (value==null)name="microsoft";elsename=value;}}}
由于屬性的方法的本質(zhì),屬性當然也有方法的種種修飾。屬性也有5種存取修飾符,但屬性的存取修飾往往為public,否則我們也就失去了屬性作為類的公共接口的意義。除了方法的多參數(shù)帶來的方法重載等特性屬性不具備外, virtual, sealed, override, abstract等修飾符對屬性與方法同樣的行為,但由于屬性在本質(zhì)上被實現(xiàn)為兩個方法,它的某些行為需要我們注意。看下面的例子:

abstract class a{int y;public virtual int x {get { return 0; }}public virtual int y {get { return y; }set { y = value; }}public abstract int z { get; set; }}class b: a{int z;public override int x {get { return base.x + 1; }}public override int y {set { base.y = value < 0? 0: value; }}public override int z {get { return z; }set { z = value; }}}
這個例子集中地展示了屬性在繼承上下文中的某些典型行為。這里,類a由于抽象屬性z的存在而必須聲明為abstract。子類b中通過base關(guān)鍵字來引用父類a的屬性。類b中可以只通過y-set便覆蓋了類a中的虛屬性。

靜態(tài)屬性和靜態(tài)方法一樣只能存取類的靜態(tài)域變量。我們也可以像做外部方法那樣,聲明外部屬性。

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 平顺县| 旺苍县| SHOW| 当涂县| 延吉市| 华阴市| 泽库县| 万荣县| 衡阳市| 金川县| 宜宾县| 常山县| 阿荣旗| 神木县| 孝感市| 辽阳县| 江川县| 扶余县| 北碚区| 特克斯县| 五家渠市| 浦北县| 库尔勒市| 开鲁县| 大田县| 武定县| 昆明市| 贵阳市| 寻乌县| 永济市| 平江县| 台州市| 弥勒县| 通辽市| 攀枝花市| 武胜县| 巴东县| 双江| 买车| 盐池县| 赫章县|