C#在类型实例化时都干了什么:从一道笔试题说开去

发表于:2012-2-08 09:47

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:cyoooo7(cnblogs)    来源:51Testing软件测试网采编

  前一阵子我参加了一次笔试,其中有一道选择题让我印象深刻,是这样的:

  实例化一个X类型对象时所执行的顺序:
  A、调用X类型构造函数,调用X类型基类的构造函数,调用X类型内部字段的构造函数
  B、调用X类型内部字段的构造函数,调用X类型基类的构造函数,调用X类型构造函数
  C、调用X类型基类的构造函数,调用X类型构造函数,调用X类型内部字段的构造函数
  D、调用X类型基类的构造函数,调用X类型内部字段的构造函数,调用X类型构造函数

  我觉的这道题出得很没水平。在C++的世界里,我会毫不犹豫的选D。但是,由于C#引入了字段初始化器,所以选什么答案完全依赖于类具体是如何设计的。好吧,我们今天就来谈谈C#在类型实例化时都有哪些步骤。

  首先我们都知道,对于类对象,在执行构造函数之前,我们需要使用关键字new来为新实例分配内存。new可以根据对象的类型来为其在堆上分配足够的空间,并且将这个对象的所有字段都设为默认值。也就是说,CLR会把该对象的所有引用类型字段设为null,而把值类型字段的所有底层二进制表示位设为0(本质上来说,不论是将值类型或引用类型字段初始化为“默认值”,其实都是把他们底层的数据位设为0)。这是任何类对象实例化的第一步。

  我们暂且先不考虑对象有指定基类的情况,先看看下面的代码吧:

class MyClass{  
  
static MyClass()
   {     
       Console.WriteLine(
"静态构造函数被调用。");  
   }
  
private static Component staticField = new Component("静态字段被实例化。");
  
private Component instanceField = new Component("实例成员字段被实例化。");
  
public MyClass()  
   {     
      Console.WriteLine(
"对象构造函数被调用。");  
   }
}

//此类型用于作MyClass类的成员//此类型在实例化的时候可以再控制台输出自定义信息,以给出相关提示
class Component
{  
  
public Component(String info)  
   {     
      Console.WriteLine(info);  
   }
}


class Program
{
  
static void Main(string[] args)
   {
      MyClass instance
= new MyClass();
   }
}

  很显然,静态构造函数和静态字段的构造函数会首先被调用。因为CLR在使用任何类型实例之前一定会先装载该类型,也就需要调用静态构造函数并且初始化静态成员。但是,到底是先初始化静态成员呢,还是调用静态构造函数呢?答案是初始化静态成员,因为CLR必须保证在执行构造函数的方法体时,相关的成员变量应该都可以被安全地使用。同样的道理也适用于实例构造函数和字段,也就是说对象成员的实例化会先于成员构造函数被执行。顺便说一句,类定义直接初始化类\对象字段的功能是由类\对象字段初始化器完成的。以下是实例化MyClass对象时控制台的输出:

静态字段被实例化。
静态构造函数被调用。
实例成员字段被实例化。
对象构造函数被调用。

  接下来,我们看看如果对象有指定的基类的情况:

class Base
{
static Base()
{
Console.WriteLine(
"基类静态构造函数被调用。");
}
 
private static Component baseStaticField = new Component("基类静态字段被实例化。");
private Component baseInstanceField = new Component("基类实例成员字段被实例化。");

public Base()
{
Console.WriteLine(
"基类构造函数被调用。");
}
}

//此类型用作派生类,同基类一样,它也包含静态构造函数,以及静态字段、实例成员字段各一个。
class Derived : Base
{
static Derived()
{
Console.WriteLine(
"派生类静态构造函数被调用。");
}

private static Component derivedStaticField = new Component("派生类静态字段被实例化。");
private Component derivedInstanceField = new Component("派生类实例成员字段被实例化。");

public Derived()
{
Console.WriteLine(
"派生类构造函数被调用。");
}
}

//此类型用于作为Base类和Derived类的成员
//此类型在实例化的时候可以在控制台输出自定义信息,以给出相关提示
class Component
{
public Component(String info)
{
Console.WriteLine(info);
}
}

//在主程序里实例化了一个子类对象
class Program
{
static void Main(string[] args)
{
Derived derivedObject
= new Derived();
}
}

21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号