菜鳥瞎扯――對象的創建過程
晚上隨便扯扯對象的創建過程,加深一下理解。主要是關于類實例化時字段的初始化過程。
示例代碼如下:
public class Class1
{
private int myInt1; //斷點1
private int myInt2 = 10;
private string myStr1;
private readonly string myStr2;
private readonly string myStr3 = "Hello";
public static int myInt3;
public static int myInt4 = 100;
private const int MY_CONST = 5;

public Class1() //斷點2
{
myInt1 = 20;
myStr2 = "World";
myStr3 = "China";
}
}

public class App
{
public static void Main(string[] args)
{
int a = 10; //斷點3
Method();
}
Public static void Method()
{
Class1 c1 = new Class1(); //斷點4
Class1 c2 = new Class1(); //斷點5
}
}
這里主要是通過加斷點調試來看Class1這個類的實例化過程,現在,按下調試按鈕:
可以看到,指針指在了斷點3處,如下圖:

此時,局部變量a的值為0,它即將要被賦以10。此時在Watch中Watch一下Class1,會發現常量MY_CONST和靜態字段myInt3、myInt4都已經有了值,并且是聲明時我們給它賦的那個值,而myInt3因為聲明時沒有賦值,所以有了默認的0(因為它們是int類型)。對于這個問題,就用《.net本質論》中的幾句話來解釋吧:
CLR會給static 字段分配一次內存:即在類型被首次加載的時候……類型被首次使用之前(靜態字段的)內存會被分配和初始化,對于其它字段(指非靜態、非常量字段),每當新的實例被分配在堆上時,內存都會被分配和初始化……常量值是在編譯時計算的。
PS: 當我們點下調試按鈕時,如果注意VS下方的狀態欄的話,會發現先是Building…然后Build Success,然后才啟動調試,有時候這些IDE做得太自動化了可能很空易就讓我這樣的菜鳥忽略了這一點。注意,源代碼是先編譯成MSIL,然后我們再去對著生成的exe來調試的,所以在編譯完,啟動調試前,MY_CONST的值就已經確定了。
回頭看,斷點1早已自動往下移了一行(調試一啟動它就移到那了)。然后點兩下step over,黃箭頭進入了Method()中,指在“斷點4”,再點一次就跳到了“斷點1”的下一行,此時c1所有字段都已用默認值初始化(包括靜態字段),如下圖:

再點一次step over,黃色箭頭指向了下圖所示位置:

此時myInt2已有了值10,這說明:當用new實例化類時,所有的字段都被初始化,如果在聲明字段時有對其賦值,如程序段中的private int myInt2 = 10;那么,這個賦值過程就發生在用默認值初始化字段的下一步。
繼續點step over,黃箭頭指在了斷點2處,此時myStr3已經有了”Hello”值,再繼續step over,構造方法中的語句將被依次執行,我們會看到,在構造方法中對myStr3這個readonly字段進行了重新賦值(如果在構造方法中還不給他賦值,那它就只好一輩子用0這個默認值了,在構造方法中對其賦值是其能被賦值的最后機會)。
然后回到Method方法中的“斷點4”處,此時狀態如下圖:

可以看到,c2雖然還沒有被實例化,但是它的靜態字段myInt4已經有值100了,其實不能這么說,myInt4不能算是c2的,而是屬于類型Class1的,它在類型首次被加載時就分配內存,初始化,靜態字段的內存分配只進行一次。
本人水平有限,以上言論也還沒有經過權威機構認證,如果大家發現其中有說錯了的地方,還請指點一下小弟,3Q VM。
示例代碼如下:
public class Class1
{
private int myInt1; //斷點1
private int myInt2 = 10;
private string myStr1;
private readonly string myStr2;
private readonly string myStr3 = "Hello";
public static int myInt3;
public static int myInt4 = 100;
private const int MY_CONST = 5;
public Class1() //斷點2
{
myInt1 = 20;
myStr2 = "World";
myStr3 = "China";
}
}
public class App
{
public static void Main(string[] args)
{
int a = 10; //斷點3
Method();
}
Public static void Method()
{
Class1 c1 = new Class1(); //斷點4
Class1 c2 = new Class1(); //斷點5
}
}
這里主要是通過加斷點調試來看Class1這個類的實例化過程,現在,按下調試按鈕:
可以看到,指針指在了斷點3處,如下圖:

此時,局部變量a的值為0,它即將要被賦以10。此時在Watch中Watch一下Class1,會發現常量MY_CONST和靜態字段myInt3、myInt4都已經有了值,并且是聲明時我們給它賦的那個值,而myInt3因為聲明時沒有賦值,所以有了默認的0(因為它們是int類型)。對于這個問題,就用《.net本質論》中的幾句話來解釋吧:
CLR會給static 字段分配一次內存:即在類型被首次加載的時候……類型被首次使用之前(靜態字段的)內存會被分配和初始化,對于其它字段(指非靜態、非常量字段),每當新的實例被分配在堆上時,內存都會被分配和初始化……常量值是在編譯時計算的。
PS: 當我們點下調試按鈕時,如果注意VS下方的狀態欄的話,會發現先是Building…然后Build Success,然后才啟動調試,有時候這些IDE做得太自動化了可能很空易就讓我這樣的菜鳥忽略了這一點。注意,源代碼是先編譯成MSIL,然后我們再去對著生成的exe來調試的,所以在編譯完,啟動調試前,MY_CONST的值就已經確定了。
回頭看,斷點1早已自動往下移了一行(調試一啟動它就移到那了)。然后點兩下step over,黃箭頭進入了Method()中,指在“斷點4”,再點一次就跳到了“斷點1”的下一行,此時c1所有字段都已用默認值初始化(包括靜態字段),如下圖:

再點一次step over,黃色箭頭指向了下圖所示位置:

此時myInt2已有了值10,這說明:當用new實例化類時,所有的字段都被初始化,如果在聲明字段時有對其賦值,如程序段中的private int myInt2 = 10;那么,這個賦值過程就發生在用默認值初始化字段的下一步。
繼續點step over,黃箭頭指在了斷點2處,此時myStr3已經有了”Hello”值,再繼續step over,構造方法中的語句將被依次執行,我們會看到,在構造方法中對myStr3這個readonly字段進行了重新賦值(如果在構造方法中還不給他賦值,那它就只好一輩子用0這個默認值了,在構造方法中對其賦值是其能被賦值的最后機會)。
然后回到Method方法中的“斷點4”處,此時狀態如下圖:

可以看到,c2雖然還沒有被實例化,但是它的靜態字段myInt4已經有值100了,其實不能這么說,myInt4不能算是c2的,而是屬于類型Class1的,它在類型首次被加載時就分配內存,初始化,靜態字段的內存分配只進行一次。
本人水平有限,以上言論也還沒有經過權威機構認證,如果大家發現其中有說錯了的地方,還請指點一下小弟,3Q VM。


浙公網安備 33010602011771號