Вопрос по static, .net, c#, clr – Потенциальные ловушки со статическими конструкторами в C #

18

Мой вопрос возникает после рефакторинга класса, который содержит только статические методы, которые должны быть объявлены какstatic класс, и возникают странные проблемы при запуске приложения.

Я не провел никакого тщательного исследования, но кажется, что какой-то вызов, сделанный из статического конструктора, по какой-то причине не завершается.

Итак, я хотел бы знать, где есть какие-либо подводные камни при использовании статических конструкторов в C #? Более конкретно, есть ли какие-либо вещи, которых следует избегать любой ценой и не использовать из статического конструктора?

Ваш Ответ

3   ответа
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 член построит ваш синглтон.

Я думаю, что это нормально, так как создание экземпляров синглетонов, если таких классов много, выполняется в правильном порядке.

Ваша инициализация не является поточно-ориентированной. ЕслиInstance Доступ к свойству одновременно осуществляется несколькими потоками, они могут получить разные экземпляры & # x201C; singleton & # x201D ;. Это может или не может быть проблемой в конкретных случаях, но это нарушает единую парадигму в целом. Если вы используете .NET 4 (или более позднюю версию), вам следует переключиться наLazy<T>; если нет, вы должны рассмотреть возможность использованияlock синхронизировать инициализацию.
@ Дуглас, спасибо. Я использую блокировку, так как я нахожусь в .net 2.0 :)
Он не отвечает на вопрос ... а статический классnot так же, как синглтон (например, вы не можете передать статический класс в качестве параметра, что вы можете сделать с синглтоном)
3

есть некоторые подводные камни, в основном связанные с инициализацией класса. По сути, класс со статическим конструктором не будет помеченbeforefieldinit флаг, который позволяет среде выполнения инициализировать его позднее.

Посмотри наЭта статья Больше подробностей.

25

р, если статический конструкторбросает исключение, вы бы продолжали получатьTypeInitializationException всякий раз, когда вы получаете доступ к любому из его членов.

If a static constructor throws an exception, the runtime will not invoke it a second time, and the type will remain uninitialized for the lifetime of the application domain in which your program is running.

Как правило, статические классы следует использовать только в сценариях без сохранения состояния, когда вам не потребуется инициализация. Если ваш класс должен быть инициализирован, вам лучше использоватьсинглтон, который может бытьлениво инициализируется при первом доступе:

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

Edit: Я сделал некоторые дальнейшие чтения по этому вопросу, и кажется, что статические конструкторыdo вызвать взаимоблокировки, если вы выполняете в них блокирующие операции (например, асинхронные обратные вызовы или синхронизацию потоков).

Среда CLR использует блокировку для предотвращения одновременного выполнения инициализаторов типов (статических конструкторов) несколько раз. Таким образом, если ваш статический конструктор попытается получить доступ к другому члену своего объявленного типа из другого потока, это неизбежно приведет к взаимоблокировке. Так как & # x201C; другой участник & # x201D; это может быть анонимная функция, объявленная как часть операции PLINQ или TPL, эти ошибки могут быть тонкими и их трудно идентифицировать.

Игорь Островский (MSFT) объясняет это в своемСтатический конструктор тупиков статья, содержащая следующий пример тупика:

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()), тупик наступает.

Есть ли что-то кроме исключений, брошенных в конструктор? Например, чем можно объяснить «тупик»? как сценарий, который я испытываю? Любая блокировка связана со статическими типами как-то за кулисами? lysergic-acid
Спасибо за полный пример! Для тех из вас, кто интересуется, является ли вышеуказанный синглтон поточно-ориентированным и «лучшим» решение (оно подразумевается, но я хочу явного), ответ - да. УвидетьJon Skeet's article on singletons in C#  или жеthis similar S.O. question.
@Mark: я считаю, что оба эквивалентны. Единственное, что делает ваш статический конструктор, это назначаетLazy в статическое поле (without инициализировать его), поэтому у него нет шансов на сбой (даже еслиMyClass() конструктор выдает исключение). В обоих случаях синглтон инициализируется (лениво) только при первомMyClass.Current называется.
Есть ли разница между тем, что у вас есть в первом примере, и этими строками?private static readonly Lazy<MyClass> current; static MyClass { current = new Lazy<MyClass>(() => new MyClass()); }   (извините, не получается правильно отформатировать :))
@liortal: ответил выше.

Похожие вопросы