C# 类型、对象、方法执行时的相互关系的一点思考

本文将从以下几个问题,来阐述C#类型、对象、方法执行时的相互关系。

new操作符的作用

CLR要求所有对象都用new操作符来创建。new操作符在创建对象的时候,具体有以下几个作用:
1.计算类型及其所有基类型中定义的所有实例字段需要的字节数。
2.从托管堆中分配类型要求的字节数,从而分配对象的内存,分配的所有字节都设为零。
3.初始化对象的“类型对象指针”和“同步块索引”成员。
4.调用类型的实例构造器,传递在new调用中指定的实参。

方法执行的过程

namespace CLR_via_Csharp {
    class People {
        public People(string name, int age) { this.name = name; this.age = age; }
        public string name;
        public int age;
        public void showInfo()
        {
            Console.WriteLine(name + ":" + age);
        }
    }

    class Program {
        static People getPeople()
        {
            People people = new People("linukey", 21);
            return people;
        }

        static void Main(string[] args)
        {
            People people = getPeople();
            people.showInfo();

            Console.ReadKey();
        }
    }
}

看以上代码,代码的最终目的就是执行getPeople与showInfo方法。我们暂时不讨论方法的类别(静态方法、实例方法等),我们讨论JIT编译器将getPeople与showInfo方法的IL代码转化成本地代码前要做的准备。
1.创建类型对象
2.编译C#代码为IL代码
第一步:
CLR要确认定义了这些类型的所有程序集都已加载。然后,利用程序集的元数据,CLR提供与这些类型有关的信息,创建一些数据结构来表示类型本身。在C#里面,类型对象本质上也是一个对象,是由System.Type类型创建的一个特殊的类型对象的实例,我们知道,堆上的所有对象都包含两个额外成员:类型对象指针和同步块索引。其中,类型对象指针就是用来查找此对象所从属的类型的。比如:People类型对象在堆上创建的对象people的实例字段中就包括一个类型对象指针,这个指针指向People类型对象,我们在使用people的GetType方法时,返回的就是指向People类型对象的类型对象指针,所以我们才能“知道”people是People类型。同理,People类型对象也含有一个实例字段就是类型对象指针,这个指针指向System.Type,因为所有的类型对象都是由System.Type类型所创建的。System.Type类型也包括一个类型对象指针,大家应该想的到,这个指针正是指向的他自己。
我们再回过头看一下我们的getPeople和showInfo两个方法,在getPeople里面,我们创建了一个people对象,people对象是People类型对象的一个实例,所以,我们要加载People类型对象到托管堆里面。在showInfo方法里面,我们有name和age两个变量,分别是Int32和String类型,我们也要对这两种类型对象进行加载。这就是第一步创建类型对象需要做的事情。
第二步:
第二步的话,.Net能够实现多语言互操作,正是因为CLS规定的一套最小功能集,.Net虚拟机可以把C#、VB.Net以及.Net里面的所有语言,都编译为IL中间语言,这样的话,就能够实现互操作。我们方法的执行,也必须经过这一步。
总结一下:当CLR确认我们方法需要的所有类型对象都已创建,代码已经编译为IL之后,就允许JIT编译器进一步把IL代码转换成面向本地特定cpu架构的本地代码,进行执行。

类型对象与对象之间的关系

在方法执行的过程第一步里面我们已经详细讨论了类型对象与对象之间的关系。这里不再赘述。

方法表

方法表是存在于类型对象的,是用来查找方法是否属于对象的一种方式。我们在执行每个对象的方法时,都要去这个对象属于的类型对象的方法表中去查找是否有此方法。

静态方法、非虚实例方法、虚实例方法执行时详细过程

1.调用静态方法时:CLR会定位与定义静态方法的类型对应的类型对象。然后,JIT编译器在类型对象的方法表中查找与被调用方法对应的记录项,对方法进行JIT编译(如果需要的话),在调用JIT编译好的方法。
2.调用非虚实例方法时:JIT编译器会找到与“发出调用的那个变量的类型”对应的类型对象。如果找到的类型对象中没有定义正在调用的那个方法,JIT编译器会回溯类层次结构(一直到Object),并在沿途的每个类型中查找该方法。之所以能够这样回溯,是因为每个类型对象都要一个字段引用了它的基类型。然后,JIT编译器在类型对象的方法表中查找引用了被调用方法的记录项,对方法进行JIT编译,再调用JIT编译好的代码。
3.调用虚实例方法时,JIT编译器要在方法中生成一些额外的代码,方法每次调用都会执行这些代码。这些代码首先检查发出调用的变量,并跟随地址来到发出调用的对象。然后,代码检查对象内部的“类型对象指针”成员,该成员指向对象的实际类型,我们上面已经讨论过了。然后,代码在类型对象的方法表中查找引用了被调用方法的记录项,对方法进行JIT编译(如果需要的话),再调用JIT编译好的代码。比如,People s = new Student();这是时候,编译器会先根据s对象的栈地址去找到对象实际所在的堆位置,然后查找对象的类型对象指针字段,找到对象实际的类型,就是Student类型,虚方法之所以会额外的执行这一步,是因为虚方法是可能会被重写的,重写的话,我们就必须确定真正的类型对象是哪个才行。

讨论到这里这篇文件基本就结束了,还有很多没完全弄明白的地方,留有以后补充。
转载请注明原地址:http://blog.csdn.net/linukey/article/details/54581111#

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值