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

问题描述 投票:0回答:6

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

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

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

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

衍生 类的 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>.Name
null
,上面的代码输出空字符串。

除了创建一些虚拟成员(例如不执行任何操作的静态

Initialize()

 方法)之外,
是否有更好的方法来确保在使用其任何派生类型之前调用基类型的静态构造函数?

如果不是,那么...就是虚拟会员!

c# .net inheritance static-constructor
6个回答
28
投票
您可以显式调用静态构造函数,因此您不必创建任何初始化方法:

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

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


20
投票
正如其他人所指出的,您的分析是正确的。该规范在这里完全按照字面意思实现;由于没有调用基类的成员并且没有创建实例,因此不会调用基类的静态构造函数。我知道这可能令人惊讶,但这是规范的严格且正确的实现。

除了“如果这样做会伤害你,就不要这样做”之外,我对你没有任何建议。我只是想指出,相反的情况也可能会咬你:

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”调用。


14
投票
这里的规则

非常复杂,在 CLR 2.0 和 CLR 4.0 之间,它们实际上以微妙而有趣的方式发生了变化,IMO 使得大多数“聪明”的方法在 CLR 版本之间变得脆弱。如果 Initialize()

 方法 
also 不涉及字段,则它可能无法在 CLR 4.0 中完成这项工作。

我会寻找替代设计,或者可能在您的类型中使用

regular延迟初始化(即检查一点或参考(针对null

)以查看是否已完成)。


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



然而,这感觉像是一种黑客行为(尽管虚拟成员也是如此)只是为了调用基本静态构造函数。


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

我的建议是避免使用静态构造函数 - 这很容易做到。还要避免静态类和静态成员(如果可能)。我并不是说永远不会,只是说要谨慎。更喜欢类的单例而不是静态类。


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); }
    
© www.soinside.com 2019 - 2024. All rights reserved.