图 7 CLI 和死锁避免
在图 7 所示的程序运行中,运行库通过允许 Static2 在 Static1 的类型构造函数完成执行之前访问 Static1 的静态 Message 属性,从而避免了死锁。Static1 中的消息应该是“Hello From Static1”,而您看到的消息却是“空白”。此程序还演示了静态字段初始值设定项如何在显式类型构造函数中的代码之前执行。
您可能认识到了,这个问题与 C++ 静态初始化失败很相似。这里的经验是避免触及类型构造函数中另一个类型的静态成员。虽然看到前面一种情形的机会很少,但是因为可用的诊断很少,所以结果很难进行调试和跟踪。
返回页首
静态反射
在运行时检查类型或对象实例的元数据的能力是一个非常强大的功能。反射使您能够构建诸如对象浏览器之类的工具,还使您能够使用在运行时插入类型的功能来创建可扩展应用程序。在类型的静态成员上使用反射技术与在对象的实例成员上进行反射有一点小小的差别。例如,图 8 中的代码将从类型 Static1 中获取静态属性 Message 的值。它还会使用 Static2 的 Type 引用来尝试获取同一个属性值。
当代码使用 GetProperties 提取 Static1 类型的属性时,它会传递绑定的标志,以表示它想要具有公共可见性的静态成员。这些标志就足以获得所需的 PropertyInfo 对象数组中 Message 属性的相关元数据了。接下来,会调用数组中第一个 PropertyInfo 对象的 GetValue 方法(在这里是 Message 属性的 PropertyInfo)。通常情况下,您需要将对象的实例作为第一个参数传递给 GetValue,但是因为这是一个静态成员,所以没有对象可传递。在使用静态属性时,您可以使用实例的一个空引用来代替。
最后,尝试使用 Static2 的 Type 对象来获取 Message 的值。静态成员不会被继承,但是 IntelliSense® 和 C# 编译器会产生继承的假象,我将稍后讨论这一点。在这种情况下,要获得 Message 属性,您需要一个额外的绑定标志 FlattenHierarchy,该标志会告诉运行库在静态成员的搜索中包括基类型。
返回页首
.NET Framework 2.0 中的静态类
目前使用的类有几个设计缺陷。即使这些类没有实例成员,您仍然可以使用 C# 或 Visual Basic 中的 new 运算符来创建这些类的实例。另外,即使您从来没有打算让这些类作为基类使用,但还是有些类会从这些类继承。
对于继承问题的解决方案是,在 C# 中应用 sealed 关键字,或在 Visual Basic 中应用 NotInheritable 关键字。为了防止从类外部的代码创建这些类(虽然它仍然会被该类的成员实例化),您可以在这些类中添加私有的默认构造函数。使用 C# 2.0 时,对这两个问题的解决方案是在类级别应用 static 关键字,如图 9 所示。
这种新语法使编译器能够强制一些新规则。您不能声明 Static1 类型的变量,因为它被标记为静态类。您也不能从 Static1 派生,或者向该类添加任何非静态成员。还要注意,从任何非对象类派生静态类都是错误的。
延伸阅读
文章来源于领测软件测试网 https://www.ltesting.net/