十九:构造器之一:实例构造器和类(引用类型)
构造器是一种允许将类型实例初始化为有效状态的特殊方法,构造器方法在方法定义元数据表中被称为.ctor。
创建引用类型的实例时,首先为实例的数据字段分配内存,接着初始化对象的系统开销字段(类型对象指针和同步块索引),最后调用类型的实例构造器设置对象的初始状态。
创建引用类型对象时,在调用类型的实例构造器之前,为对象分配的内存始终被清零,构造器没有显示赋值的所有字段保证都有一个0或null值。
实例构造器不能被继承,也就是说,类只能有自己定义的构造器,不能继承父类的,所以构造器不能用以下修饰符:virtual,new,override,sealed,abstract。
编译器为我们自动的生成一个无参构造器,该构造器的实现只是为了调用基类的无参构造器。
如果类的修饰符为abstract,那么编译器生成的默认构造器的可访问性为protected,否则,构造器的可访问性为public。如果基类没有提供无参构造器,那么派生类必须显式地调用基类的构造器。如果类的修饰符为static,sealed和abstract,编译器就不会在类的定义中生成一个默认的构造器。
一个类型可以定义多个实例构造器,而且每个构造器可以有不同的可访问性。
C#语言还提供了一种简单的语法,允许在构建类型实例的过程中初始化类型中定义的字段,如下:
internal sealed class SomeType
{
private Int32 m_x = 10;
}
构建SomeType对象时,SomeType对象的m_x字段将被初始化为10,查看一下SomeType的构造器方法(.ctor)的中间语言,如下图:
可以看到SomeType先把10存储到m_x字段,接着它又调用了基类构造器,也就是说C#编译器提供了这种更方便的语法来内联初始化实例字段,并且将这个语法转换成构造器方法中的代码来执行初始化,这时候就要注意到代码膨胀效应,看下面代码:
internal sealed class SomeType
{
private Int32 m_x = 10;
private String m_s = "test";
private Double m_d = 3.141519;
private Byte m_b;
public SomeType() { }
public SomeType(Int32 x) { }
public SomeType(String s)
{
//...
m_d = 10;
}
}
编译器为上述三个构造器方法生成代码时,每个方法入口都包含初始化m_x,m_s,m_d的代码,在这些代码初始化之后,编译器将构造器方法中的代码附加到方法后面。如果有几个已初始化的实例字段和许多重载的构造器方法,应考虑在定义字段时避免同时对它们进行初始他,而是将这些公共初始化语句放在一个单独的构造器中,然后让其它构造器来显式的调用这个公共初始化构造器,这样减少所生成的代码大小,以下例子就是用this关键字来调用另一个构造器的方法示例:
internal sealed class SomeType
{
//不要显示的初始化下面字段
private Int32 m_x;
private String m_s;
private Double m_d;
private Byte m_b;
//该构造器将所有的字段都设为默认值,其它构造器必须显式的调用这个构造器
public SomeType()
{
m_x = 10;
m_s = "test";
m_d = 3.141519;
m_b = oxff;
}
//该构造器首先将所有字段都设为默认值,然后再修改m_x
public SomeType(Int32 x)
: this()
{
m_x = x;
}
//该构造器首先将所有字段都设为默认值,然后再修改m_s
public SomeType(String s)
{
m_s = s;
}
//该构造器首先将所有字段都设为默认值,然后再修改m_x和m_s
public SomeType(Int32 x, String s)
: this()
{
m_x = x;
m_s = s;
}
}