(学习手册自:框架设计 CLR Via C#)
8.1实例构造器和类:P175
实例构造器不能被继承,也就是说类只能拥有自己定义的实例构造器,许多编辑器定义一个默认的无参构造器,它的实现只是调用基类的无参构造函数
如下例:
public class SomeType{}相当于public class SomeType{ public SomeType():base(){} }
如果类的修饰符为abstract,那么编译器生成的默认构造器将为protected,否则为public
如果类的修饰符为static,那么编译器根本不会在类的构造器中生成一个默认的构造器
如非必要我们可以不向一个类提供构造函数,编译器会在后台创建一个默认的构造函数。但如果提供了自定义的构造函数,编译器就不会提供默认的构造函数。
例:http://www.cnblogs.com/xiongpq/archive/2010/08/18/1801965.html
自己写的例子:
class Test
{
static void Main(string[] args)
{
Test2 tests = new Test2(1, 2);
Console.WriteLine("OK");
Console.ReadKey();
}
}
class Test1
{
public Test1(int a, int b)
{
Console.Write("aa" + (a + b));
}
}
class Test2 : Test1
{
public Test2(int a, int b): base(a, b)//如果不写将默认为base(),此时如果基类中不存在无参构造函数,将会报错,因为基类如果提供了自定义的构造函数,编译器就不会提供默认的无参构造函数了
{
Console.Write(a + b);
}
}
如果一个类中存在多个构造器和实例字段,应考虑在定义实例字段的时候避免同时对他们进行初始化(内联初始化),如果给他们初始化的同时,在构造器中又再次对他们进行赋值,会出现代码膨胀现象,如此例:
class SomeType
{
//不要显式初始化下面的字段
private Int32 m_x=1;
private String m_s="2";
private Double m_d=3.0;
private Byte m_b=1;
//该构造器将所有的字段都设为默认值。所有其他构造器都必须显式调用这个构造器
public SomeType()
{
m_x = 5;
m_s = "Hi there";
m_d = 3.14159;
m_b = 0xff;
}
//该构造器首先将所有的字段都设为默认值,然后修改m_x
public SomeType(Int32 x)
: this()
{
m_x = x;
}
//该构造器首先将所有的字段都设为默认值,然后修改m_s
public SomeType(String s)
: this()
{
m_s = s;
}
//该构造器首先将所有的字段都设为默认值,然后修改m_x和m_s
public SomeType(Int32 x, String s)
: this()
{
m_x = x;
m_s = s;
}
static void Main(string[] args)
{
}
}
此时它的无参构造函数中生成的IL代码为(代码膨胀94):
当不给上述的实例字段进行初始化时,生成的IL代码如下(54):
8.2值类型构造器:P179
1.公共语言运行库允许创建值类型的实例,无法阻止其初始化,编译器不会为值类型生成无参构造函数,C#不允许值类型生成无参构造函数
2.CLR允许值类型定义有参构造器,但是执行值类型上定义的构造器的唯一方法是编写代码显示的调用这些构造器
3.虽然C#不允许值类型拥有无参构造器,但是CLR允许值类型拥有无参构造器(C#不能在值类型中内联实例字段的初始化)
internal struct SomeValueType { private Int32 x= 5;//不能在值类型中内联实例字段的初始化 }
4.可验证代码要求值类型的每个字段都要在值类型的任何字段被读取之前进行初始化,所
以值类型的构造器必须初始化值类型的所有字段。
8.3类型构造器(静态构造器):P180
1.类型构造器是用来设置类型的初始状态,默认情况下不在类型内部定义类型构造器
2.类型构造器只能有一个且永远没有参数,当类第一次被访问时执行
3.类型构造器不允许出现访问修饰符,且默认为私有的
4.C#允许在值类型中定义无参类型构造器,但永远也不要定义,因为CLR中存在不调用
值类型类型构造器的情况,如下例:
5.C#允许值类型使用内联初始化语法初始化静态字段
如 struct/class SomeType{ private static Int32 x=5; }
相当于:
Struct/Class SomeType{ private static Int32 x; static SomeType(){x=5;} }
注意:类型构造器不调用基类型的类型构造器
类型构造器的性能(P183):
8.3节中我们提到对类型构造器的调用是一件棘手的事情,并且说明了一些棘手之处:JIT编译器必须决定是否生成调用类型构造器的代码;CLR要确保对类型构造器的调用是线程安全的。事实证明,这只是一个开头,与类型构造器性能相关的棘手事情还有很多
JIT编译器需要决定它是否必须生成一个调用来执行方法中的类型构造器。如果JIT编译器决定生成该调用,那么,JIT编译器必须决定在什么地方生成调用,有以下两种情况:
A. JIT编译器可以在首次创建类型实例的代码之前或者在访问类的非继承字段或成员的代码之前立即生成调用。因为CLR会在确定的时刻调用类型构造器,所以这种情况被称为精确语义(precise semantic)。
B. JIT编译器有时可以在访问非继承静态字段的代码前生成调用。因为CLR确保只有静态构造器可以在静态字段被访问之前的某个时候运行(静态构造器可能会很早运行),所以这种情况称为字段初始化前语义(before-field-init semantic)。
通俗的解释:
类型构造器只调用一次,JIT编译器需要在适当的时候确定生成调用类型构造器的代码,有以上两种地方可以生成。字段初始化前语义(隐式构造器)会在生成的类的类型定义条目表中标记一个BeforeFieldInit元数据标记,精确语义(显示构造器)不会
例:
internal sealed class BeforeField
{
public static Int32 s_x = 123;
}
//因为这个类确实显式定义了类型构造器,所以C#在元数据中没有用BeforeFieldInit标识来标记
//类型定义
internal sealed class Precise1
{
public static Int32 s_x;
static Precise1() { s_x = 123; }
}
public sealed class Program
{
static Program(){}
public static void Main()
{
const Int32 iterations = 1000 * 1000 * 1000;
PerfTest1(iterations);
PerfTest2(iterations);
}
//当这个方法被JIT编译时,BeforeField和Precise1类的类型构造器还没有被执行,因此,
//这些构造器的调用将内嵌在该方法的代码中,从而使运行速度变慢
private static void PerfTest1(Int32 iterations)
{
Stopwatch sw = Stopwatch.StartNew();
for (Int32 x = 0; x < iterations; x++)
{
//JIT编译器将下述代码提前执行以调用BeforeField的类型构造器,以便它在循
//环开始前执行
BeforeField.s_x = 1;
}
Console.WriteLine("PerfTest1: {0} BeforeField", sw.Elapsed);
sw = Stopwatch.StartNew();
for (Int32 x = 0; x < iterations; x++)
{
//JIT编译器在这里生成调用Precise1类的类型构造器的代码,以便它检查是否对每一次
//循环迭代都必须调用构造器
Precise1.s_x = 1;
}
Console.WriteLine("PerfTest1: {0} Precise1", sw.Elapsed);
}
//当这个方法被JIT编译时,BeforeField和Precise1类的类型构造器已经被执行,因此,
//这些构造器的调用没有内嵌在该方法的代码中,从而使运行速度变快
private static void PerfTest2(Int32 iterations)
{
Stopwatch sw = Stopwatch.StartNew();
for (Int32 x = 0; x < iterations; x++)
{
BeforeField.s_x = 1;
}
Console.WriteLine("PerfTest2: {0} BeforeField", sw.Elapsed);
sw = Stopwatch.StartNew();
for (Int32 x = 0; x < iterations; x++)
{
Precise1.s_x = 1;
}
Console.WriteLine("PerfTest2: {0} Precise1", sw.Elapsed);
}
}
生成的定义表条目如下图所示: