C#中静态构造函数的潜在缺陷

我的问题来自重构一个只包含静态方法的类被声明为static类,并在启动应用程序时遇到奇怪的问题。

我没有进行任何彻底的调查,但似乎从静态构造函数中进行的某些调用由于某种原因没有完成。

那么,我想知道在C#中使用静态构造函数时有哪些陷阱? 更具体地说,有什么东西应该不惜一切代价避免,而不是在静态构造函数中使用?

静态构造函数有几个缺陷。 例如,如果静态构造函数抛出exception , TypeInitializationException只要访问其任何成员,就会继续获取TypeInitializationException

如果静态构造函数抛出exception,则运行时将不再调用它,并且该类型将在运行程序的应用程序域的生命周期内保持未初始化状态。

通常,静态类只应在无需初始化的无状态场景中使用。 如果你的类需要初始化,你可能最好使用单例模式 ,它可以在第一次访问时被懒惰地初始化 :

 public class MyClass { private static readonly Lazy current = new Lazy(() => new MyClass()); public static MyClass Current { get { return current.Value; } } private MyClass() { // Initialization goes here. } public void Foo() { // ... } public void Bar() { // ... } } static void Main(string[] args) { MyClass.Current.Foo(); // Initialization only performed here. MyClass.Current.Bar(); MyClass.Current.Foo(); } 

编辑 :我做了一些关于此事的进一步阅读,如果你在其中执行阻塞操作(例如异步回调或线程同步),静态构造函数似乎确实会导致死锁。

CLR内部使用锁定来防止类型初始化器(静态构造函数)同时执行多次。 因此,如果您的静态构造函数尝试从另一个线程访问其声明类型的另一个成员,它将不可避免地死锁。 由于“另一个成员”可能是一个声明为PLINQ或TPL操作一部分的匿名函数,因此这些错误可能很微妙且难以识别。

Igor Ostrovsky(MSFT)在他的Static构造函数死锁文章中解释了这一点,提供了以下死锁示例:

 using System.Threading; class MyClass { static void Main() { /* Won't run... the static constructor deadlocks */ } static MyClass() { Thread thread = new Thread(arg => { }); thread.Start(); thread.Join(); } } 

在上面的示例中,新线程需要访问定义为其回调的空匿名函数{ } 。 但是,由于匿名函数在后台编译为MyClass另一个私有方法,因此新线程在MyClass类型初始化之前无法访问它。 并且,由于MyClass静态构造函数需要等待新线程首先完成(因为thread.Join() ),因此会出现死锁。

是的,有一些陷阱,主要与课程初始化时有关。 基本上,具有静态构造函数的类将不会使用beforefieldinit标志进行标记,该标志允许运行时稍后对其进行初始化。

有关更多详细信息,请查看此文章 。

这不是问题的答案,但评论太长了,所以我在这里提供……

由于我不知道static class构造,我使用了以下方案(简化)来为我提供单例:

 public class SomeSingleton { static _instance; static public SomeSingleton Instance { get { if (_instance==null) { _instance=new SomeSingleton(); } return _instance; } } } 

后来,你用

 SomeSingleton.Instance.MyProp = 3; 

首次使用Instance成员将构建您的单例。

我想如果有很多这样的类以正确的顺序完成,那么单例实例化就可以了。