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

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

public class Class1
{
    private string name;
    private int age;
}

类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时对其初始化:

public class Class1
{
   private string name = "Lin";
   private int age;
}

再用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#程序显式加上一个构造方法,它接受两个参数:

.cctor

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

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

public class Class1
{
        private string name = "Lin";
        public static int count = 50;
        private int age;

        public Class1(string name, int age)
        {
            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#中就是加个静态构造函数,我们不能为其指定访问修饰符(否则编译就会报错):

public class Class1
{
        private string name = "Lin";
        public static int count = 50;
        private int age;

        static Class1()
        {
             count = 100;
         }

        public Class1(string name, int age)
        {
            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。

public class Class1
{
        private string name = "Lin";
        private int age;

        public Class1(string name, int age)
        {
            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)中最先执行,然后再执行其基类的构造方法,最后才轮到我们显示定义的构造方法体中代码。

在继承中对象构造过程

看下面这段程序:

    public class A
    {
        public int x = 1;
        public A() { m1(); }
        public void m1() { }
     }

    public class B : A
    {
        public int y = 2;
        public static string sb = "B";
        public B() { m2(); }
        public void m2() { }
     }

    public class C : B
    {
        public int z = 3;
        public static string sc = "C";
        public C() { m3(); }
        public void m3() { }
     }

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

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

先看类C的.ctor:

IL_0000:  ldarg.0
  IL_0001:  ldc.i4.3
  IL_0002:  stfld      int32 CLRviaCSharp.Chapter5.ClassInherit.C::z
  IL_0007:  ldarg.0
  IL_0008:  call       instance void CLRviaCSharp.Chapter5.ClassInherit.B::.ctor()
  IL_000d:  nop
  IL_000e:  nop
  IL_000f:  ldarg.0
  IL_0010:  ldc.i4.5
  IL_0011:  stfld      int32 CLRviaCSharp.Chapter5.ClassInherit.C::z
  IL_0016:  ldarg.0
  IL_0017:  call       instance void CLRviaCSharp.Chapter5.ClassInherit.C::m3()


可以看到:

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

再来看类B的.ctor():

IL_0000:  ldarg.0
  IL_0001:  ldc.i4.2
  IL_0002:  stfld      int32 CLRviaCSharp.Chapter5.ClassInherit.B::y
  IL_0007:  ldarg.0
  IL_0008:  call       instance void CLRviaCSharp.Chapter5.ClassInherit.A::.ctor()
  IL_000d:  nop
  IL_000e:  nop
  IL_000f:  ldarg.0
  IL_0010:  call       instance void CLRviaCSharp.Chapter5.ClassInherit.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的显式构造方法中的代码,就是这么一个递归的过程。

原文URL:http://hi.baidu.com/0_aymy/blog/item/5c9f47aef4f6fbf2faed50c5.html

转载于:https://www.cnblogs.com/jeriffe/articles/1962262.html

IL语言之.ctor相关推荐

  1. c il语言 定义变量,[转载]Skill语言入门

    第一章SKILL 语言基础 1.1 SKILL 语言简介 SKILL 是用于Cadence 软件二次开发的语言.我们关于SKILL 的介绍基于SKILL 语言参考档和Allegro SKILL的应用. ...

  2. [你必须知道的.NET]第十三回:从Hello, world开始认识IL

    发布日期:2007.7.22 作者:Anytao ©2007 Anytao.com ,原创作品,转贴请注明作者和出处. 本文将介绍以下内容: IL代码分析方法 Hello, world历史 .NET学 ...

  3. [你必须知道的.NET]第二十五回:认识元数据和IL(中)

    说在,开篇之前 书接上回[第二十四回:认识元数据和IL(上)],我们对PE文件.程序集.托管模块,这些概念与元数据.IL的关系进行了必要的铺垫,同时顺便熟悉了以ILDASM工具进行反编译的基本方法认知 ...

  4. [你必须知道的.NET]第十四回:认识IL代码---从开始到现在

    本文将介绍以下内容: ·       IL代码分析方法 ·       IL命令解析 ·       .NET学习方法论 1. 引言 自从『你必须知道.NET』系列开篇以来,受到大家很多的关注和支持, ...

  5. 开放源码的.NET 反编译工具 .NET IL调试工具 学习微软中间语言(MSIL)的绝佳工具 Dotnet IL Editor 推荐...

    Dotnet IL Editor是一款.NET平台反编译工具,可以反编译.NET程序集文件为IL代码,并且可以执行,调试反编译后生成的IL代码.它的设计出发点比较直观,新建一个项目,添加程序集文件,设 ...

  6. ILSpy可以反编译c语言,ILSpy反编译工具的使用

    本文导读:ILSpy反编译工具的使用.不过,ILspy需要在电脑上安装.NET Framework 4.0.ILspy可以将一个dll文件转换为C#或VB语言.ILspy对于单个文件可以保存为.cs文 ...

  7. CLR中的IL、CTS和CLS总结

    1,IL我们称之为高级汇编语言或者中间语言,在我们编写基于.NET框架下的程序(不论是C#.VB或者J#等)在被编译成本地CPU指令前,都将被编译成IL语言,再有CLR对IL语言进行编译和控制 比如用 ...

  8. 初读CLR Via C# 之 IL、CTS、CLS

    本章将对CLR中的IL.CTS和CLS进行一个大概的总结.下章将开始第二章的内容. IL我们称之为高级汇编语言或者中间语言,在我们编写基于.NET框架下的程序(不论是C#.VB或者J#等)在被编译成本 ...

  9. IL应用之——用IL分析接口的本质

    IL反编译利器--Ildasm.exe和Reflector.exe: 一:Ildasm.exe简介 这一微软VS自带工具,在上一篇博文<初识Ildasm.exe--IL反编译的实用工具>中 ...

最新文章

  1. Python环境安装与配置
  2. 人月神话阅读笔记 03
  3. 2020蓝桥杯省赛---java---B---8(走方格)
  4. Mongodb 分片与副本集
  5. 【转】聊聊分布式事务,再说说解决方案
  6. OSPF工作机制——OSPF邻居状态机详解(附图)
  7. java 反射覆盖方法,java – 确定一个方法是否覆盖使用反射的另一个?
  8. android碎片按钮,Android 碎片学习 之 如何在碎片调用Activity中的方法来进行赋值操作...
  9. 解决office 2013 新建PPT(幻灯片)为拉伸矩形问题
  10. ESP8266热点配网-Arduino代码分享
  11. 视频教程-【曾贤志】Excel函数视频教程-Office/WPS
  12. Google技巧:crack web sites
  13. kali linux捆绑工具shellter的安装及使用
  14. day51 列表、表格、form标签
  15. STM32 CAN通信之二:正常模式
  16. linux内存镜像技术,GitHub - oscomp/proj61-linux-address-range-memory-mirroring: Linux 内核内存镜像功能增强。...
  17. 大数据课程体系-学习笔记概要
  18. ftp服务器覆盖文件,ftp 覆盖 服务器 文件
  19. 数据库系统实践 IV 查询插入修改删除操作
  20. 英文SEO采集伪原创软件Kontent Machine注册使用实战教程!

热门文章

  1. Hi3516A开发-- 常见问题FAQs
  2. 用区块链改变人工智能:去中心化带来数据新范式
  3. 如何在solaris下安装memcached
  4. linux make编译卡死,为linux内核编译make文件时出现问题?
  5. JZOJ 5454. 【NOIP2017提高A组冲刺11.5】仔细的检查
  6. JZOJ 5443. 【NOIP2017提高A组冲刺11.2】字典序
  7. JZOJ 4909. 【NOIP2017模拟12.3】李电下棋
  8. java异常捕获常使用的语句_要点Java14 捕获异常
  9. html4.0编辑器,KindEditor HTML(在线编辑器)
  10. 2020年, VQA论文汇总