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

时间:2011-01-10 22:55:32

标签: c# .net inheritance static-constructor

documentation on static constructors in C#说:

  

使用静态构造函数   初始化任何静态数据,或者   执行需要的特定操作   仅执行一次。它被称为   在第一个之前自动   实例已创建或任何静态   成员被引用

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

之前运行。

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

为了使这更具体一点,我以下代码可行:

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);
}

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

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

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

6 个答案:

答案 0 :(得分:23)

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

System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof (TypeBase).TypeHandle);

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

答案 1 :(得分: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"); }
}

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

答案 2 :(得分:13)

这里的规则are very complex,以及CLR 2.0和CLR 4.0之间实际上changed in subtle and interesting ways,IMO使CLR版本之间的大多数“聪明”方法变得脆弱。 {CLIN 4.0中的Initialize()方法可能无法完成该工作,如果它没有触及字段。

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

答案 3 :(得分:3)

在我的所有测试中,我只能调用基类上的虚拟成员,使基类调用其静态构造函数,如图所示:

class Base
{
    static Base()
    {
        Console.WriteLine("Base static constructor called.");
    }

    internal static void Initialize() { }
}

class Derived : Base
{
    static Derived()
    {
        Initialize(); //Removing this will cause the Base static constructor not to be executed.
        Console.WriteLine("Derived static constructor called.");
    }

    public static void DoStaticStuff()
    {
        Console.WriteLine("Doing static stuff.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Derived.DoStaticStuff();
    }
}

另一个选项是在派生类型中包含一个静态只读成员,它执行以下操作:

private static readonly Base myBase = new Base();

然而,感觉就像一个hack(虽然虚拟成员也是这样)只是为了调用基本的静态构造函数。

答案 4 :(得分:3)

我几乎总是后悔依赖这样的事情。静态方法和类可以在以后限制您。如果你想稍后为你的Type类编写一些特殊行为,你将被装箱。

所以这里的方法略有不同。这是一个更多的代码,但它允许你有一个后来定义的自定义类型,让你做自定义的东西。

    abstract class TypeBase
    {
        private static bool _initialized;

        protected static void Initialize()
        {
            if (!_initialized)
            {
                Type<int>.Instance = new Type<int> {Name = "int"};
                Type<long>.Instance = new Type<long> {Name = "long"};
                Type<double>.Instance = new Type<double> {Name = "double"};
                _initialized = true;
            }
        }
    }

    class Type<T> : TypeBase
    {
        private static Type<T> _instance;

        public static Type<T> Instance
        {
            get
            {
                Initialize();
                return _instance;
            }
            internal set { _instance = value; }
        }

        public string Name { get; internal set; }
    }

然后,当您开始向Type添加虚拟方法并希望为Type提供特殊实现时,您可以实现:

class TypeInt : Type<int>
{
    public override string Foo()
    {
        return "Int Fooooo";
    }
}

然后通过更改

将其连接起来
protected static void Initialize()
{
      if (!_initialized)
      {
          Type<int>.Instance = new TypeInt {Name = "int"};
          Type<long>.Instance = new Type<long> {Name = "long"};
          Type<double>.Instance = new Type<double> {Name = "double"};
          _initialized = true;
       }
}

我的建议是避免使用静态构造函数 - 这很容易。还要避免使用静态类和静态成员。我不是说永远,只是谨慎。首选类的单例为静态。

答案 5 :(得分:0)

只是一个想法,你可以做这样的事情:

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

    class Type<T> : TypeBase
    {
        static Type() 
        {
            new Type<object>();
        }

        public static string Name { get; internal set; }
    }

    class Program
    {
        Console.WriteLine(Type<int>.Name);
    }