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