.ctor:

简述:构造函数,在类被实例化时,它会被自动调用。

当C#的类被编译后,在IL代码中会出现一个名为.ctor的方法,它就是我们的构造函数,对应C#中的构造函数。且看下面的代码:

publicclassClass1

{

privatestringname;

privateintage;

}

类Class1中没有显示的构造函数,只有两字段,现在用ILDasm.exe打开编译后生成的exe文件,会看到:

可以看到这里有个.ctor,我们没有定义构造函数,但这里却出现了.ctor,这就说明了:

当没有显示定义构造函数时,会自动生成一个构造函数,它没有参数,没有返回值。

那我们来看看这个.ctor都干了什么吧,双击.ctor打开,在弹出的窗口中可以找到下面的几行代码:

IL_0000: ldarg.0

IL_0001: call       instance void [mscorlib]System.Object::.ctor()

IL_0006: ret

上面就是这个.ctor的方法体,看上面的红色行,从字面上可以看出,它是调用(call)了一个类型为System.Object的实例的.ctor()方法,从这就可以证明:

当一个类没有显示声明继承于其它某个类时,它将默认继承自System.Object,并且,在类的构造函数中将会调用其基类的构造方法(.ctor)。

现在对上面的程序小改一下,在声明name时对其初始化:

publicclassClass1

{

privatestringname="Lin";

privateintage;

}

再用ILDasm打开生成的exe文件,打开.ctor,里面有这么几行:

IL_0000: ldarg.0

IL_0001: ldstr      "Lin"

IL_0006: stfld      string ConsoleApplication1.Class1::name

IL_000b: ldarg.0

IL_000c: call       instance void [mscorlib]System.Object::.ctor()

IL_0011: nop

这个跟刚才的相比,多出了红色的那两行,这两行出现在“调用System.Object的构造方法”之前,这说明:

如果在字段声明的同时对其初始化,那么在编译后,赋值过程将被放到构造方法.ctor中,并且在调用其基类的构造方法之前进行。现在给上面的C#程序显式加上一个构造方法,它接受两个参数:

publicclassClass1

{

privatestringname="Lin";

privateintage;

publicClass1(stringname,intage)

{

this.name=name;

this.age=age;

        }}

再用ILDasm打开exe时,会发现有了点变化:

这里的.ctor带了两参数,一个string类型,一个int32类型,而刚才的无参无返回值的.ctor不见了,这也证明了:

如果类中有显式定义构造方法,那么就不会再自动生成一个无参数无返回值的默认构造方法。

打开.ctor,会看到其中有这么几行:

IL_0000:  ldarg.0

IL_0001: ldstr      "Lin"

IL_0006: stfld      string ConsoleApplication1.Class1::name

IL_000b: ldarg.0

IL_000c: call       instance void [mscorlib]System.Object::.ctor()

IL_0011: nop

IL_0012: nop

IL_0013: ldarg.0

IL_0014: ldarg.1

IL_0015: stfld      string ConsoleApplication1.Class1::name

IL_001a: ldarg.0

IL_001b: ldarg.2

IL_001c: stfld      int32 ConsoleApplication1.Class1::age

IL_0021: nop

从上面红色标识的代码的顺序中,我们可以进一步得到:

如果在声明字段时同时对其赋值,那么这个赋值过程将在类型的构造方法(.ctor)中最先执行,然后再执行其基类的构造方法,最后才轮到我们显示定义的构造方法体中代码。

.cctor

简述:类型初始化器,是一个静态方法,无参数无返回值,不能直接调用,最多只有一个

我们现在先给刚才的代码加上一个静态字段:

publicclassClass1

{

privatestringname="Lin";

publicstaticintcount=50;

privateintage;

publicClass1(stringname,intage)

{

this.name=name;

this.age=age;

        }}

再来打开ILDasm来看看:

发现这里多了一个.cctor,它就是类型初始化器,打开它,会看到其中有一句:

IL_0000: ldc.i4.s   50

IL_0002: stsfld     int32 ConsoleApplication1.Class1::count

它对静态字段count进行了赋值,值是50,那么,是.cctor先调用还是.ctor先调用呢?当然是.cctor,它是为初始化类型而生的,专搞静态的东东,而.ctor是构造方法,当然.cctor要先调用了。

现在显示加上一个.cctor,在C#中就是加个静态构造函数,我们不能为其指定访问修饰符(否则编译就会报错):

publicclassClass1

{

privatestringname="Lin";

publicstaticintcount=50;

privateintage;

staticClass1()

{

            count=100;

        }

publicClass1(stringname,intage)

{

this.name=name;

this.age=age;

        }}

再来看看现在ILDasm下的.cctor,其中有几行:

IL_0000: ldc.i4.s   50

IL_0002: stsfld     int32 ConsoleApplication1.Class1::count

IL_0007: nop

IL_0008: ldc.i4.s   100

IL_000a: stsfld     int32 ConsoleApplication1.Class1::count

可以看到:

如果在声明静态字段时同时对其赋值,它在编译后会被搬到.cctor中,并且是放在前面,然后才到显式定义的静态构造方法体中的代码,也就是说count在这里会被赋值两次,第一次50,第二次100。

在继承中对象构造过程

看下面这段程序:

publicclassA

{

publicintx=1;

publicA(){ m1(); }

publicvoidm1(){ }    }

publicclassB : A

{

publicinty=2;

publicstaticstringsb="B";

publicB(){ m2(); }

publicvoidm2(){ }    }

publicclassC : B

{

publicintz=3;

publicstaticstringsc="C";

publicC(){ m3(); }

publicvoidm3(){ }    }

编译后用ILDasm打开生成的exe文件:

可以看到三者都有一个.ctor,B、C中有.cctor,而A没有,打开B,C的.cctor,可以看到它们都负责初始化自己的静态字段,现在主要来看它们的.ctor。

先看类C的.ctor:

IL_0001: ldc.i4.3

IL_0002: stfld      int32 ConsoleApplication1.C::z

IL_0007: ldarg.0

IL_0008: call       instance void ConsoleApplication1.B::.ctor()

IL_000d: nop

IL_000e: nop

IL_000f: ldarg.0

IL_0010: call       instance void ConsoleApplication1.C::m3()

可以看到:

在C被实例化时,它最先初始化在声明时同时赋值的字段(非静态),此处是将3赋给z,然后它会调用其基类的.ctor(),然后再调用自己的实例方法m3(),值得注意的是,在执行显式定义的构造方法体中的代码前,会先调用其基类的构造方法(创建基类的实例)。再来看类B的.ctor():

IL_0001: ldc.i4.2

IL_0002: stfld      int32 ConsoleApplication1.B::y

IL_0007: ldarg.0

IL_0008: call       instance void ConsoleApplication1.A::.ctor()

IL_000d: nop

IL_000e: nop

IL_000f: ldarg.0

IL_0010: call       instance void ConsoleApplication1.B::m2()

同样,我们可以看到,在实例化B时,它会先把2赋给自己的y,然后再调用基类A的构造方法,最后再调用自己的实例方法m2()。

那A的.ctor()就不再看了,可以猜到它一定是在做这样的事:

1、 将1赋给实例的x字段;

2、 调用基类System.Object的构造方法.ctor来创建基类的实例;

3、 调用实例方法m1();

总结

1、.ctor是构造方法;

2、.cctor是类型初始化器,在C#中也就是静态构造函数;

3、当类C实例化时,会先对声明时就进行赋值的字段赋值,然后调用基类的构造函数,基类再以同样的方法构造自己,一直到顶层的System.Object,然后再回来执行C的显式构造方法中的代码,就是这么一个递归的过程。

参考资料

1、《Essential .NET》 Volume 1

java ctor_.ctor,.cctor 以及 对象的构造过程相关推荐

  1. Java基础教程-05-面向对象

    Java基础教程-05-面向对象 1. 面向过程和面向对象解释 1.1 面向过程 1.1.1 简述 我们回想一下, 前面我们完成一个需求的步骤是怎样的? 首先是搞清楚我们要做什么. 然后在分析怎么做. ...

  2. 对象 普通po转_谈谈C++对象的构造

    对 象 造化从来自有神 如何对此亦无尘 平生出处皆非妄 老去功名始见真 这是小编以"构造对象"为主题让九歌同学创作的七言绝句.九歌同学以一种非常玄妙的文风向我们介绍了对象的构造,但 ...

  3. $emit传递多个参数_10年架构师深解java核心技术:方法参数+对象构造,确定不学?...

    方法参数 首先回顾一下在程序设计语言中有关参数传递给方法(或函数)的一些专业术语.值调用(call by value)表示方法接收的是调用者提供的值.而引用调用(call by reference)表 ...

  4. 嵌入式软件开发培训笔记——Java第三天(方法重载、对象的构造与初始化过程分析、封装等)

    一.掌握方法重载(Overload)     同一个类中方法名相同时,称为方法的重载(Overload)     特点:1.参数列表不同                     1)参数类型不同   ...

  5. Java 对象的构造和初始化

    对象的构造 package w5;class ConstructCallThisAndSuper {public static void main(String[] args){ Person p = ...

  6. Java学习总结:51(对象序列化)

    对象序列化 对象序列化的本质实际上就是将内存中所保存的对象数据转换为二进制数据流进行传输的操作. 但并不是所有类的对象都可以直接进行序列化操作,要被序列化的对象所在的类一定要实现java.io.Ser ...

  7. java初始化实例化_Java对象的创建过程:类的初始化与实例化

    一.Java对象创建时机 我们知道,一个对象在可以被使用之前必须要被正确地实例化.在Java代码中,有很多行为可以引起对象的创建,最为直观的一种就是使用new关键字来调用一个类的构造函数显式地创建对象 ...

  8. Java 对象初始化的过程介绍

    在Java中,一个对象在可以被使用之前必须要被正确地初始化,这一点是Java规范规定的.在实例化一个对象时,JVM首先会检查相关类型是否已经加载并初始化,如果没有,则JVM立即进行加载并调用类构造器完 ...

  9. Java I/O中的对象序列化

    Java I/O中的对象序列化 Java对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,并能够以后将这个字节序列完全恢复为原来的对象.利用对象的序列化,可以实现轻量级持久性 ...

最新文章

  1. 1.4 Padding-深度学习第四课《卷积神经网络》-Stanford吴恩达教授
  2. DNA Sorting
  3. 征战蓝桥 —— 2018年第九届 —— C/C++B组第1题——第几天
  4. python Typing模块-类型注解
  5. 乐玩自动化测试模块_自动化测试模型(一)自动化测试模型介绍
  6. TTSR再次理解,主要是针对Texture Transformer TT的全部过程的一个梳理
  7. stm32 hal 串口只可以接收到一包数据数据
  8. 树的非递归(前序,中序,后序)
  9. linux命令获取显示器信息,如何确定液晶显示器是否从Linux命令行打开
  10. Java接口与实现类的转换
  11. 网易云音乐代码如何写入html,如何将网易云音乐加入到自己的网站!
  12. easyRobot工业机器人系统
  13. MNIST手写数据,从训练到数据预测(keras)
  14. idea顶部工具栏、底部工具栏、两边工具栏的显示与隐藏
  15. Vue中router-link路由跳转以及传参方式
  16. GCD深入学习之GCD的初识
  17. linux彻底清除历史记录
  18. 投资学翻译2 Digesting Anomalies An Investment Approach
  19. ┊非主流图片┊非主流美女┊非主流照片
  20. python发邮件,添加附件

热门文章

  1. 服务器上的文件都变成只读了,[求助]Excel 文档都变成只读了,请问怎么恢复?/excle变成只读文件怎么办...
  2. Prometheus+SpringBoot应用监控全过程详解
  3. 文案排版(参考中文文案排版指北)
  4. TW6869 drivers porting for freescaleandroid
  5. 容错性低是什么意思_王者荣耀:在成为高手之前,这4位容错率低的千万别碰!...
  6. Tether聘请前银行分析师首席合规官
  7. [附案例]如何运营私域流量,提升用户终身价值,降低整体获客成本?
  8. 2017第九届北京空气净化及水净化、北京国际节能减排展览会会刊
  9. poscms清除html,poscms购物
  10. 微信小程序实现微信APP上的扫一扫扫码跳到小程序对应的结果页面和签字等功能