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