实例构造器和类(引用类型)
构造器是允许将类型的实例初始化为良好状态的一种特殊方法。构造器方法在“方法定义元数据表”中始终教.ctor。
创建一个引用类型的实例时,首先为实例的数据字段分配内存,然后初始化对象的附加字段(类型对象指针呵呵同步块索引),最后调用类型的实例构造器来设置对象的初始状态。
实例构造器永远不能被继承。
如果类的修饰符为abstract,那么编译器生成的默认构造器的可访问性就为protected;否则,构造器会被赋予public可访问性。如果基类没有提供无参数构造器,那么派生类必须显示调用一个基类构造器,否则编译器会报错。如果类的修饰符为static(sealed和abstract),编译器根本不会再类的定义中生成一个默认构造器。
C#编译器提供一个简化的语法,允许以“内联”方式初始化实例字段。但在幕后,它会将这种语法转换成构造器方法中的代码来执行初始化。这同时提醒我们注意代码的膨胀效应。
所谓内联,是指在代码中直接赋值来初始,而不是通过构造器。
internal sealed class SomeType
{
public SomeType() { }
public SomeType(Int32 x) { }
public SomeType(String s) { }
private Int32 m_x = 5;
private String m_s = "Hello World";
private Double m_d = 3.14159;
private Byte m_b;
}
编译器为这三个构造器方法生成代码时,在每个方法开始的位置,都会包含用于初始化m_x,m_s和m_d的代码。在这些初始化代码之后,编译器会插入对基类构造器的调用。再然后,会插入构造器自己的代码。查看该类的IL代码,发现有三个构造方法:
每个.ctor方法中都会初始化m_x,m_s和m_d。如下IL代码(这里只是贴出了无参数构造器的IL代码):
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// 代码大小 43 (0x2b)
.maxstack 8
IL_0000: ldarg.0
IL_0001: ldc.i4.5
IL_0002: stfld int32 ctor.SomeType::m_x
IL_0007: ldarg.0
IL_0008: ldstr "Hello World"
IL_000d: stfld string ctor.SomeType::m_s
IL_0012: ldarg.0
IL_0013: ldc.r8 3.1415899999999999
IL_001c: stfld float64 ctor.SomeType::m_d
IL_0021: ldarg.0
IL_0022: call instance void [mscorlib]System.Object::.ctor()
IL_0027: nop
IL_0028: nop
IL_0029: nop
IL_002a: ret
} // end of method SomeType::.ctor
如果有几个以初始化的实例字段和大量重载的构造器方法,可考虑不是在定义字段时初始化,而是创建单个构造器来执行这些公共的初始化。然后,让其他构造器显示调用这个公共初始化构造器。这样可以减少生成的代码。如下演示:
public SomeType1()
{
m_x = 5;
m_s = "Hello World";
m_d = 3.14159;
m_b = 0xff;
}
public SomeType1(Int32 x)
: this()
{
m_x = x;
}
public SomeType1(String s)
: this()
{
m_s = s;
}
public SomeType1(Int32 x, String s)
: this()
{
m_s = s;
m_x = x;
}
}
编译后,会发现无参数的构造函数IL代码与上面的IL一样,而在SomeType1(Int32 x)中的IL代码中如下:
.method public hidebysig specialname rtspecialname
instance void .ctor(int32 x) cil managed
{
// 代码大小 17 (0x11)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void ctor.SomeType1::.ctor()
IL_0006: nop
IL_0007: nop
IL_0008: ldarg.0
IL_0009: ldarg.1
IL_000a: stfld int32 ctor.SomeType1::m_x
IL_000f: nop
IL_0010: ret
} // end of method SomeType1::.ctor
在该构造器中先调用无参数的构造器(如上红色),然后再进行本身构造器的实例化。
由此可见,这样很好的防止了代码膨胀。