C#編譯器是否會為實值型別產生預設的建構函式呢?
答案是否定的,C#編譯器並不會為值產生預設的建構函式的,這樣設計一方面是基於效能方面的考慮,另外一個原因是實值型別可以被隱式的建立!這一點可以通過ILDasm工具開驗證,實值型別確實沒有產生預設的建構函式,看下面的代碼:
class Program
{
static void Main(string[] args)
{
A a =new A();
}
}
struct A
{
}
有人可能比較奇怪:A a=new A();這行是需要調用結構體的預設建構函式的,如果沒有產生預設的建構函式,這裡應該同不過編譯的,事實正如大家預料的,這行代碼可以通過編譯的!
這裡似乎有自相矛盾的地方啊!先別急,我們先看看Main方法的IL代碼:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// 代碼大小 10 (0xa)
.maxstack 1
.locals init ([0] valuetype ConsoleApplication1.A a)
IL_0000: nop
IL_0001: ldloca.s a
IL_0003: initobj ConsoleApplication1.A
IL_0009: ret
} // end of method Program::Main
我們看紅色的程式碼,第一行是把局部變數a入棧,第二行是初始化a,注意使用的是initobj指令,這裡調用的是隱式的預設的建構函式,因為每種實值型別均有一個隱式的預設建構函式來初始化該類型的預設值!
如果換成參考型別應該使用newobj指令,這時調用C#編譯器產生的預設的建構函式了!
實際上這也就是為什麼在實值型別中的成員聲明的同時不能直接初始化的原因,因為C#編譯器根本沒有為實值型別產生預設的建構函式,讓他何處初始化呢!
希望這篇文章能夠對大家有一點協助啊!
感謝大家的參與啊!
我在這裡統一回複一下大家:
一樓說的對,在C#中,結構體是不允許定義無參的建構函式的,只能定義有參數的建構函式的,這麼設計重要是為了避免引起程式員的誤解!
比如這樣:
Class MyClass
{
public A a;//可能有的人會誤認為構造MyClass執行個體的時候會自動調用A的預設的建構函式,一般有C++的經驗的會有這樣的想法,這裡C#和C++不一樣
}
為了避免這種混淆,C#中並不允許在結構體中提供預設的無參建構函式!
關於結構體的用途,可能大家有一定誤解!實際上在C#中結構體是很有用的,並不是僅僅為了相容過去的遺留組件而存在。
下面我詳細說說:
大家都知道結構體是實值型別的,所以他的執行個體是存放在棧中的,而且棧的訪問速度是比託管堆要快的,但是空間沒有堆大!
所以可以把一些比較小的類型定義為結構來提高訪問的速度,例如:
struct Point
{
int x;
int y;
}
大家可能想既然訪問速度比較快,為什麼不全部類型都定義為實值型別的呢?道理比較簡單,棧的空間並不是很大,不能存放很多的東西,另外在棧中即使是值一樣的執行個體,往往也需要存放兩份副本的,比如:
Point p1=new Point();
Point p2=p1;
在棧中就有兩份p1的拷貝!
這樣本身也比較占空間!
所以只有一些本身比較小的類型適合定義成結構體,大的類型最好還是定義為參考型別,放到空間相對比較大的託管堆中比較好!
再次感謝大家參與討論啊!謝謝大家!