結構是使用 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
int i = 1;
object o = i; //隐式裝箱
i = 123;
Console.WriteLine("i={0},o={1}",i,o);
//結果為:i=123,o=1
結構與構造函數
我們知道結構不能使用預設的構造函數,隻能使用帶參數的構造函數,當定義帶參數的構造函數時,一定要完成結構所有字段的初始化,如果沒有完成所有字段的初始化,編譯時會發生錯誤-。 結構可以使用靜态構造函數嗎? 可以,結構的靜态構造函數與類的靜态構造函數所遵循的規則大體相同。 結構的靜态構造函數何時将觸發呢? 結構的執行個體成員被引用,結構的靜态成員被引用,結構顯示聲明的構造函數被調用。
但是建立結構類型的預設值不會觸發靜态構造函數。為什麼結構不能自定義無參數的構造函數? 結構類型的構造函數與類的構造函數類似,用來初始化結構的成員變量,但是struct不能包含顯式預設構造函數, 因為編譯器将自動提供一個構造函數,此構造函數将結構中的每個字段初始化為預設值表中顯示的預設值。 然而,隻有當結構用new執行個體化時,才會調用此預設構造函數。對值類型調用預設構造函數不是必需的。
static A()
Console.WriteLine("I am A.");
public void Fun()
A a=new A();
a.Fun(); //結構的執行個體成員被引用
結果為: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);
A a; //結構的執行個體化可以不使用new
a.Fun(1, 2);
// 結果為:x=1,y=2
什麼情況下結構的執行個體化可以不使用new?
當結構中沒有參數時,結構的執行個體化可以不使用new;
當結構中有參數時,必須對結構中所有參數進行初始化後,才能不使用new對結構進行執行個體化。
什麼時候使用結構?
結構體适合一些小型資料結構,這些資料結構包含的資料以建立結構後不修改的資料為主;
例如:struct類型适于表示Point、Rectangle和Color等輕量對象。
盡管可以将一個點表示為類,但在某些情況下,使用結構更有效。
如果聲明一個10000個Point對象組成的數組,為了引用每個對象,則需配置設定更多記憶體;這種情況下,使用結構可以節約資源。-
定義的時候不會用到面向對象的一些特性;
結構體在不發生裝箱拆箱的情況下性能比類類型是高很多的.