淺談C#結(jié)構(gòu)
結(jié)構(gòu)是使用 struct關(guān)鍵字定義的,與類相似,都表示可以包含數(shù)據(jù)成員和函數(shù)成員的數(shù)據(jù)結(jié)構(gòu)。
一般情況下,我們很少使用結(jié)構(gòu),而且很多人也并不建議使用結(jié)構(gòu),但作為.NET Framework 一般型別系統(tǒng)中的一個基本架構(gòu),還是有必要了解一下的。
結(jié)構(gòu)的特征:
結(jié)構(gòu)是一種值類型,并且不需要堆分配。結(jié)構(gòu)的實例化可以不使用new運算符。
在結(jié)構(gòu)聲明中,除非字段被聲明為 const 或 static,否則無法初始化。結(jié)構(gòu)類型永遠不是抽象的,并且始終是隱式密封的,因此在結(jié)構(gòu)聲明中不允許使用abstract和sealed修飾符。
結(jié)構(gòu)不能聲明默認構(gòu)造函數(shù)(沒有參數(shù)的構(gòu)造函數(shù))或析構(gòu)函數(shù),但可以聲明帶參數(shù)的構(gòu)造函數(shù)。結(jié)構(gòu)可以實現(xiàn)接口,但不能從另一個結(jié)構(gòu)或類繼承,而且不能作為一個類的基,所有結(jié)構(gòu)都直接繼承自 System.ValueType,后者繼承自 System.Object。結(jié)構(gòu)在賦值時進行復(fù)制。將結(jié)構(gòu)賦值給新變量時,將復(fù)制所有數(shù)據(jù),并且對新副本所做的任何修改不會更改原始副本的數(shù)據(jù)。在使用值類型的集合(如 Dictionary<string, myStruct>)時,請務(wù)必記住這一點。結(jié)構(gòu)類型的變量直接包含了該結(jié)構(gòu)的數(shù)據(jù),而類類型的變量所包含的只是對相應(yīng)數(shù)據(jù)的一個引用(被引用的數(shù)據(jù)稱為“對象”)。但是結(jié)構(gòu)仍可以通過ref和out參數(shù)引用方式傳遞給函數(shù)成員。結(jié)構(gòu)可用作可以為 null 的類型,因而可向其賦 null 值。structA
- {publicintx; //不能直接對其進行賦值publicinty;
 - public static stringstr = null; //靜態(tài)變量可以初始化publicA(intx,inty) //帶參數(shù)的構(gòu)造函數(shù)
 - {this.x =x;this.y =y;
 - Console.WriteLine("x={0},y={1},str={2}", x, y,str);
 - }
 - }classProgram
 - {staticvoidMain(string[] args)
 - {
 - A a =newA(1,2);
 - A a1 =a;
 - a.x =10;
 - Console.WriteLine("a1.x={0}",a1.x);
 - Console.Read();
 - }
 - }
 
結(jié)果為:x=1,y=2,str=
a1.x=1
此時a1.x值為1是因為,將a賦值給a1是對值進行復(fù)制,因此,a1不會受到a.x賦值得改變而改變。
但如果A是類,這時a和a1里的x引用的是同一個地址,則a1.x的值會輸出10。
結(jié)構(gòu)的裝箱與拆箱我們知道,一個類類型的值可以轉(zhuǎn)換為object類型或由該類實現(xiàn)的接口類型,這只需在編譯時把對應(yīng)的引用當(dāng)作另一個類型處理即可。
與此類似,一個object類型的值或者接口類型的值也可以被轉(zhuǎn)換回類類型而不必更改相應(yīng)的引用。當(dāng)然,在這種情況下,需要進行運行時類型檢查。由于結(jié)構(gòu)不是引用類型,上述操作對結(jié)構(gòu)類型是以不同的方式實現(xiàn)的。
當(dāng)結(jié)構(gòu)類型的值被轉(zhuǎn)換為object類型或由該結(jié)構(gòu)實現(xiàn)的接口類型時,就會執(zhí)行一次裝箱操作。
反之,當(dāng) object類型的值或接口類型的值被轉(zhuǎn)換回結(jié)構(gòu)類型時,會執(zhí)行一次拆箱操作。
與對類類型進行的相同操作相比,主要區(qū)別在于:
裝箱操作會把相關(guān)的結(jié)構(gòu)值復(fù)制為已被裝箱的實例,而拆箱則會從已被裝箱的實例中復(fù)制出一個結(jié)構(gòu)值。
因此,在裝箱或拆箱操作后,對“箱”外的結(jié)構(gòu)進行的更改不會影響已被裝箱的結(jié)構(gòu)。structProgram
- {staticvoidMain(string[] args)
 - {inti =1;objecto =i; //隱式裝箱
 - i =123;
 - Console.WriteLine("i={0},o={1}",i,o);
 - Console.Read();
 - }
 - }
 - //結(jié)果為:i=123,o=1
 
結(jié)構(gòu)與構(gòu)造函數(shù)我們知道結(jié)構(gòu)不能使用默認的構(gòu)造函數(shù),只能使用帶參數(shù)的構(gòu)造函數(shù),當(dāng)定義帶參數(shù)的構(gòu)造函數(shù)時,一定要完成結(jié)構(gòu)所有字段的初始化,如果沒有完成所有字段的初始化,編譯時會發(fā)生錯誤。結(jié)構(gòu)可以使用靜態(tài)構(gòu)造函數(shù)嗎?
可以,結(jié)構(gòu)的靜態(tài)構(gòu)造函數(shù)與類的靜態(tài)構(gòu)造函數(shù)所遵循的規(guī)則大體相同。
結(jié)構(gòu)的靜態(tài)構(gòu)造函數(shù)何時將觸發(fā)呢?結(jié)構(gòu)的實例成員被引用,結(jié)構(gòu)的靜態(tài)成員被引用,結(jié)構(gòu)顯示聲明的構(gòu)造函數(shù)被調(diào)用。但是創(chuàng)建結(jié)構(gòu)類型的默認值不會觸發(fā)靜態(tài)構(gòu)造函數(shù)。
為什么結(jié)構(gòu)不能自定義無參數(shù)的構(gòu)造函數(shù)?
結(jié)構(gòu)類型的構(gòu)造函數(shù)與類的構(gòu)造函數(shù)類似,用來初始化結(jié)構(gòu)的成員變量,但是struct不能包含顯式默認構(gòu)造函數(shù),
因為編譯器將自動提供一個構(gòu)造函數(shù),此構(gòu)造函數(shù)將結(jié)構(gòu)中的每個字段初始化為默認值表中顯示的默認值。
然而,只有當(dāng)結(jié)構(gòu)用new實例化時,才會調(diào)用此默認構(gòu)造函數(shù)。對值類型調(diào)用默認構(gòu)造函數(shù)不是必需的。
- structA
 - {staticA()
 - {
 - Console.WriteLine("I am A.");
 - }publicvoidFun()
 - {
 - }
 - }classProgram
 - {staticvoidMain(string[] args)
 - {
 - A a=newA();
 - a.Fun(); //結(jié)構(gòu)的實例成員被引用
 - Console.Read();
 - }
 - }
 
結(jié)果為:I am A.
結(jié)構(gòu)與繼承:
一個結(jié)構(gòu)聲明可以指定實現(xiàn)的接口列表,但是不能指定基類。
由于結(jié)構(gòu)不支持類與結(jié)構(gòu)的繼承,所以結(jié)構(gòu)成員的聲明可訪問性不能是protected或protectedinternal。結(jié)構(gòu)中的函數(shù)成員不能是abstract或 virtual,因而override修飾符只適用于重寫從System.ValueType繼承的方法。
為在設(shè)計編程語言時將結(jié)構(gòu)設(shè)計成無繼承性?
其實類的繼承是有相當(dāng)?shù)某杀镜?——由于繼承性,每個類需要用額外的數(shù)據(jù)空間來存儲“繼承圖”來表示類的傳承歷史,
通俗地說來就是我們?nèi)祟惖募易寮易V,里面存儲著我們的祖宗十八代,只有這樣我們才知道我們從哪里來的,而家譜肯定是需要額外的空間來存放的。
大家不要覺得這個存放“繼承圖”的空間很小,如果我們的程序需要用10000個點(Point)來存放游戲中的人物形體數(shù)據(jù)的話,
在一個場景中又有N個人,這個內(nèi)存開銷可不是小數(shù)目了。所以我們可以通過將點(Point)申明成 Struct而不是class來節(jié)約內(nèi)存空間。interfaceITest
- {voidFun(intx,inty);
 - }structA:ITest
 - {publicvoidFun(intx,inty) //隱式實現(xiàn)接口里的方法
 - {
 - Console.WriteLine("x={0},y={1}", x, y);
 - }
 - }classProgram
 - {staticvoidMain(string[] args)
 - {
 - A a; //結(jié)構(gòu)的實例化可以不使用new
 - a.Fun(1, 2);
 - Console.Read();
 - }
 - }
 - // 結(jié)果為:x=1,y=2
 
什么情況下結(jié)構(gòu)的實例化可以不使用new?
當(dāng)結(jié)構(gòu)中沒有參數(shù)時,結(jié)構(gòu)的實例化可以不使用new;當(dāng)結(jié)構(gòu)中有參數(shù)時,必須對結(jié)構(gòu)中所有參數(shù)進行初始化后,才能不使用new對結(jié)構(gòu)進行實例化。什么時候使用結(jié)構(gòu)?
結(jié)構(gòu)體適合一些小型數(shù)據(jù)結(jié)構(gòu),這些數(shù)據(jù)結(jié)構(gòu)包含的數(shù)據(jù)以創(chuàng)建結(jié)構(gòu)后不修改的數(shù)據(jù)為主;
例如:struct類型適于表示Point、Rectangle和Color等輕量對象。
盡管可以將一個點表示為類,但在某些情況下,使用結(jié)構(gòu)更有效。
如果聲明一個10000個Point對象組成的數(shù)組,為了引用每個對象,則需分配更多內(nèi)存;這種情況下,使用結(jié)構(gòu)可以節(jié)約資源。
定義的時候不會用到面向?qū)ο蟮囊恍┨匦裕?/p>
結(jié)構(gòu)體在不發(fā)生裝箱拆箱的情況下性能比類類型是高很多的.
原文鏈接:http://www.cnblogs.com/jiajiayuan/archive/2011/09/20/2181582.html
【編輯推薦】















 
 
 
 
 
 
 