确保基类的静态构造函数被调用的最佳方法是什么?

Dan*_*Tao 35 .net c# inheritance static-constructor

C#中有关静态构造函数文档说:

静态构造函数用于初始化任何静态数据,或执行仅需执行一次的特定操作.在创建第一个实例或引用任何静态成员之前自动调用它.

最后一部分(关于何时被自动调用)让我陷入了一个循环; 直到阅读那部分我认为通过简单地以任何方式访问一个类,我可以确定它已经调用了它的基类的静态构造函数.测试和检查文档表明情况并非如此; 看起来基本类的静态构造函数不能保证在访问该基类的成员之前运行.

现在,我想在大多数情况下,当您处理派生类时,您将构造一个实例,这将构成正在创建的基类的实例,因此将调用静态构造函数.但是,如果我只处理派生类的静态成员,那么呢?

为了使这更具体一点,我认为下面的代码可以工作:

abstract class TypeBase
{
    static TypeBase()
    {
        Type<int>.Name = "int";
        Type<long>.Name = "long";
        Type<double>.Name = "double";
    }
}

class Type<T> : TypeBase
{
    public static string Name { get; internal set; }
}

class Program
{
    Console.WriteLine(Type<int>.Name);
}
Run Code Online (Sandbox Code Playgroud)

我假设访问Type<T>该类会自动调用静态构造函数TypeBase; 但事实并非如此.Type<int>.Namenull,并且上面的代码输出空字符串.

除了创建一些虚拟成员(就像Initialize()什么都不做的静态方法)之外,还有更好的方法来确保在使用任何派生类型之前调用基类型的静态构造函数吗?

如果没有,那么......虚拟成员就是!

pet*_*kyy 24

您可以调用静态构造函数表达式,因此您不必创建任何初始化方法:

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof (TypeBase).TypeHandle);
Run Code Online (Sandbox Code Playgroud)

您可以在派生类的静态构造函数中调用它.

  • +1这似乎有效.与任何静态初始化顺序解决方案一样,我怀疑它是否是防弹的,但至少如果你在一个简单的单线程应用程序中调用两次,它似乎不会运行静态ctor两次. (4认同)

Eri*_*ert 17

正如其他人所说,你的分析是正确的.规范在这里完全实现; 由于没有调用基类的成员且没有创建实例,因此不调用基类的静态构造函数.我可以看到这可能是多么令人惊讶,但它是一个严格和正确的规范实现.

除了"如果你这样做会伤害,不要这样做",我对你没有任何建议.我只想指出相反的情况也可以咬你:

class Program 
{
  static void Main(string[] args)
  {      
    D.M();
  }      

}
class B 
{ 
  static B() { Console.WriteLine("B"); }
  public static void M() {}
} 
class D: B 
{ 
  static D() { Console.WriteLine("D"); }
}
Run Code Online (Sandbox Code Playgroud)

尽管已经调用了"D的成员",但这打印"B".M仅通过继承而成为D的成员; CLR无法区分BM是"通过D"还是"通过B".


Mar*_*ell 13

这里的规则非常复杂,在CLR 2.0和CLR 4.0之间,它们实际上以微妙而有趣的方式发生了变化,IMO使得大多数"聪明"的方法在CLR版本之间变得脆弱.一种Initialize()方法可能不会做的工作在CLR 4.0,如果它不触及领域.

我会寻找替代设计,或者在你的类型中使用常规的延迟初始化(即检查一下或引用(反对null)以查看它是否已经完成).