先看代码:

namespace Temp
{
    class Program
    {
        static void Main(string[] args)
        {
            Class1 c = new Class1();
        }
    }

    class BaseClass
    {
        int z = 3;

        public BaseClass()
        {
            MethodA();
        }

        public virtual void MethodA()
        {
            Console.WriteLine("BaseClass.MethodA");
        }
    }

    class Class1 : BaseClass
    {
        int x = 1;
        int y;

        public Class1()
        {
            y = 2;
        }

        public override void MethodA()
        {
            Console.WriteLine(x + y);
        }
    }
}

以上是一个简单的继承层次结构。不要使用 VS 测试,脑子分析一下最终输出了什么?

分析过程中脑子存在任何疑问的同学,请马上动手测试一下吧,在 Main 方法中设个断点单步跟踪一下。

这里描述一下单步调试的整个过程:

  1. 黄色光标进入 Class1 类时跳入了第一句 int x = 1;
  2. 黄色光标跳过第二句 int y 指向 Class1 的构造函数;
  3. 在执行构造函数的代码块之前跳入了父类,黄色光标指向父类 BaseClass 的 int z = 3 语句;
  4. 黄色光标指向 BaseClass 的构造函数;
  5. 黄色光标指向构造函数内的 MethodA() 调用;
  6. 黄色光标跳向子类 Class1 重写的方法 MethodA();
  7. 查看两个字段发现 x=1, y=0;
  8. 黄色光标指向 Console 语句;
  9. 黄色光标从父类构造函数的 MethodA() 调用中跳出;
  10. 黄色光标从父类构造函数跳出,并再次指向子类构造函数,执行完其中的代码块;
  11. 直至执行完毕。

 

这里说明了几个顺序问题:

  1. 对于直接赋值的字段的赋值步骤是在构造函数之前执行,子类字段的赋值是在父类的字段赋值之前;
  2. 对于字段的内存分配、初始化等步骤是在我们所能看到的黄色光标进入 Class1 类的步骤之前;
  3. 执行构造函数前会首先执行父类的构造函数;
  4. 执行构造函数时 CLR 已能识别方法的覆写情况,表明方法的加载过程是在对字段的赋值步骤之前;
  5. int 类型的字段在分配内存、初始化阶段已默认赋了 0 值(仅限字段中的 int,方法中的int变量并非如此)。

总结:当执行 new 语句时,发生了以下几件事情(更细的情形本文暂不探讨):

  1. 为字段分配内存并进行初始化;
  2. 如果类是第一次加载(即系统中从未创建类的其它对象,或者曾经创建但因不再有引用而被 GC 全部回收),则 Copy 其实例方法至方法表;
  3. 为类中需要直接赋值的字段赋值;
  4. 执行构造函数。

这里只分析了字段类型为 int 的情形,欢迎大家自己探索 string 或其它引用类型的情形。欢迎多多指教。

PS: 请教一下,VS中单步调试时的黄色光标有没有专用术语?

posted on 2010-10-12 17:23  飞笑  阅读(1292)  评论(0编辑  收藏  举报