Ответ 1
Есть несколько подводных камней для статических конструкторов. Например, если статический конструктор генерирует исключение, вы будете продолжать получать TypeInitializationException
всякий раз, когда вы обращаетесь к любому из его членов.
Если статический конструктор создает исключение, среда выполнения не будет вызывать его второй раз, и тип останется неинициализированным для времени жизни домена приложения, в котором работает ваша программа.
В общем, статические классы должны использоваться только в сценариях без состояния, где вам не нужна инициализация. Если ваш класс должен быть инициализирован, вам может быть лучше использовать шаблон лениво инициализируется при первом доступе:
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();
}
Изменить. Я прочитал еще несколько вопросов по этому вопросу, и кажется, что статические конструкторы вызывают взаимоблокировки, если вы выполняете блокирующие операции (например, асинхронные обратные вызовы или синхронизацию потоков) внутри них.
CLR внутренне использует блокировку для предотвращения одновременного выполнения нескольких инициализаторов типов (статических конструкторов). Таким образом, если ваш статический конструктор пытается получить доступ к другому члену своего типа объявления из другого потока, это неизбежно затормозит. Поскольку "другой член" может быть анонимной функцией, объявленной как часть операции PLINQ или TPL, эти ошибки могут быть тонкими и трудно идентифицируемыми.
Игорь Островский (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()
), произойдет затвор.