欢迎来到.net学习网

欢迎联系站长一起更新本网站!QQ:879621940

您当前所在位置:首页 »  .NET本质论第一卷:公共语言运行库教程 » 正文

本教程章节列表
最新评论

类型和初始化

创建时间:2012年08月28日 16:11  阅读次数:(6016)
分享到:
在决定讨论类型成员之前,有两个方法需要引起特别关注。类型允许提供一个特别方法,在它首次被初始化时调用。这个类型初始化器是一个简单的静态方法,它有一个众所周知的名字(.cctor)。一个类型最多只有一个类型初始化器,它没有参数和返回值,也不能被直接调用。它们是被CLR作为类型初始化的一部分自动调用的。每种编程语言提供了自己的语法,用于定义类型初始化器。在VB.NET中,你只用简单地写一个名为New 的Shared(按类型访问)子程序;在C#中,你可以写一个静态方法,它的名字与声明类型的名字相同,但没有返回值。下面是一个用C#写的类型初始化器:
namespace AcmeCorp.LOB {
public sealed class Customer {
internal static long t;
static Customer() { // 这就是类型的初始化器!
t = System.DateTime.Now.Ticks;
}
}
}

这段代码语义等价于下面的类型定义,它使用了C#字段初始化表达式,而不是显式的类型初始化器:
namespace AcmeCorp.LOB {
public sealed class Customer {
internal static long t = System.DateTime.Now.Ticks;
}
}

对于这两种情况,作为结果的CLR类型都将有一个类型初始化器。在前一种情况下,你可以把任意语句放到初始化器中。而对于后一种情况,你只能用初始化表达式。但在这两种情况下,最后的结果类型都会有同样.cctor方法,并且,t字段在访问之前被初始化。
有趣的是,单个C#类型既可以有显式的类型初始化方法,又能有带初始化表达式的静态字段声明,而这是合法的。当两者都存在时,作为结果的.cctor方法将以字段初始化开始(按照声明的顺序),接下来是显式的类型初始化方法体。

考虑下面的C#类型定义:
namespace AcmeCorp.LOB {
public sealed class Customer {
static Customer() {
t1 = System.DateTime.Now.Ticks;
Debug.Assert(t2 <= t3); // 明显
Debug.Assert(t3 <= t1); // 不是很明显
}
internal static long t1;
internal static long t2 = System.DateTime.Now.Ticks;
internal static long t3 = System.DateTime.Now.Ticks;
}
}

根据这个类型定义,字段将以这个顺序进行初始化:t2,t3,t1。

至于类型初始化器实际运行的时机,CLR将灵活处理。类型初始化器总是保证在首次访问类型的静态字段之前执行。除此之外,CLR还支持两种策略:默认策略是在首次访问类型的任何成员之前,执行类型初始化器;第二种策略(通过beforefieldinit元数据特性标明)给CLR更大的灵活性。标记为beforefieldinit的类型与没有标记的类型在两个方面是不同的。其一,在第一个成员被访问前,CLR将充分拥有调用类型初始化器的主动权;其二,CLR会推迟对于类型初始化器的调用,直到有一个静态字段被首次访问之时。这意味着在beforefieldinit类型上调用静态方法,并不保证类型初始化器会执行。它同时还说明,在类型初始化器执行以前,就可以自由地创建实例并使用它。也就是说,CLR将保证在任何方法使用到一个静态字段之前,执行类型初始化器。

C#编译器会在所有缺乏显式类型初始化器方法的类型上,设置一个beforefieldinit特性。而带有显式的类型初始化器方法的类型将不会被设置这个元数据特性。在静态字段声明中存在初始化表达式,将不会影响C#编译器是否使用beforefieldinit特性。

前面的讨论主要是用于类型初始化的特别方法,这些方法由CLR调用,并作为类型初始化的一部分。当类型的实例每次被分配时,CLR将自动调用另外一个不同的方法。这个方法被称为构造函数(constructor),并有一个截然不同的名字.ctor。构造函数不像类型初始化器,它可以接受它想要的参数。此外,它还能够使用方法重载的规则,即一个类型可以提供多个重载的构造函数方法。不带任何参数的构造函数被称为类型的默认构造函数。为了授予或禁止对个别成员的访问,构造函数方法还可以使用访问修饰符,它们与字段或者标准的方法使用的修饰符是一样的。这与类型初始化方法有很大不同,类型初始化方法总是private。

每种语言都提供了它自己的语法用于编写构造函数。在VB.NET中,可以写一个名为New的子程序(non-Shared)。在C#和C++中,可以写一个(non-static)方法名与声明的类型名相同但没有返回值的方法。

下面是一个带两个构造函数的C#类型的例子:
namespace AcmeCorp.LOB {
public sealed class Customer {
internal long t1;
internal long t2 = System.DateTime.Now.Ticks;
internal long t3 = System.DateTime.Now.Ticks;
public Customer() {
t1 = System.DateTime.Now.Ticks;
}
public Customer(long init) {
t1 = init;
}
}
}

C#编译器将在所生成的.ctor方法中,在显式的方法体之前,插入non-static字段初始化表达式。就默认构造函数来说,t2和t3初始化语句会在t1的初始化语句之前。
C#编译器还支持链式(chaining)构造函数,允许一个构造函数调用另一个构造函数。下面的类型定义,使用了构造函数链,这个例子与先前的例子在语义上是相同的。
namespace AcmeCorp.LOB {
public sealed class Customer {
internal long t1;
internal long t2 = System.DateTime.Now.Ticks;
internal long t3 = System.DateTime.Now.Ticks;
public Customer()
: this(System.DateTime.Now.Ticks)?// 调用下面的ctor 
{
}
public Customer(long init) {
t1 = init;
}
}
}

注意用于链式构造函数的语法是语言相关的。C#以外的语言可参照相应的语言参考。
来源:.net学习网
说明:所有来源为 .net学习网的文章均为原创,如有转载,请在转载处标注本页地址,谢谢!
【编辑:Wyf】

打赏

取消

感谢您的支持,我会做的更好!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

最新评论

共有评论0条
  • 暂无任何评论,请留下您对本文章的看法,共同参入讨论!
发表评论:
留言人:
内  容:
请输入问题 46+31=? 的结果(结果是:77)
结  果: