菜鸟瞎扯――对象的创建过程
晚上随便扯扯对象的创建过程,加深一下理解。主要是关于类实例化时字段的初始化过程。
示例代码如下:
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。
示例代码如下:

































这里主要是通过加断点调试来看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。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架