我的问题是在重构了一个只包含要声明为static
类的静态方法的类之后,并且在启动应用程序时遇到了奇怪的问题。
我没有进行任何彻底的调查,但似乎是静态构造函数中的某些调用由于某种原因而无法完成。
所以,我想知道在C#中使用静态构造函数时有哪些陷阱?更具体地说,是否有任何事情应该不惜一切代价避免,而不是在静态构造函数中使用?
答案 0 :(得分:25)
静态构造函数有几个缺陷。例如,如果是静态构造函数throws an exception,只要您访问其任何成员,就会继续获得TypeInitializationException
。
如果静态构造函数抛出异常,则运行时将不会再次调用它,并且该类型将在运行程序的应用程序域的生命周期内保持未初始化状态。
通常,静态类只应在无需初始化的无状态场景中使用。如果您的课程需要初始化,最好使用singleton pattern,首次访问时可以lazily initialized:
public class MyClass
{
private static readonly Lazy<MyClass> current =
new Lazy<MyClass>(() => 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();
}
编辑:我做了一些关于此事的进一步阅读,如果执行阻塞操作(例如异步回调或线程同步),静态构造函数 do 会导致死锁)在他们里面。
CLR内部使用锁定来防止类型初始化器(静态构造函数)同时执行多次。因此,如果您的静态构造函数尝试从另一个线程访问其声明类型的另一个成员,则它将不可避免地死锁。由于“另一个成员”可能是一个声明为PLINQ或TPL操作一部分的匿名函数,因此这些错误可能很微妙且难以识别。
Igor Ostrovsky(MSFT)在他的Static constructor deadlocks文章中解释了这一点,提供了以下死锁示例: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()
),因此会出现死锁。
答案 1 :(得分:3)
是的,有一些陷阱,主要与课程初始化时有关。基本上,具有静态构造函数的类不会标记beforefieldinit
标志,这允许运行时稍后对其进行初始化。
有关详细信息,请查看this article。
答案 2 :(得分:0)
这不是问题的答案,但是评论太长了,所以我在这里提供......
由于我不知道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
成员将构建您的单身人士。
我认为,如果有很多这样的类以正确的顺序完成,那么单例实例化就可以了。