2014-12-15 3 views
9

У меня есть класс, определяемый как:Детских статический конструктор не вызывается, когда базовый элемент доступен

public class DatabaseEntity<T> where T : DatabaseEntity<T> { 
    public static string Query { get; protected set; } 
    public static IList<T> Load() { 
     return Database.Get(Query); 
    } 
} 

public class Node : DatabaseEntity<Node> { 
    static Node() { 
     Node.Query = @"SELECT Id FROM Node"; 
    } 
} 

Когда я бег Node.Load() из (Window.xaml.cs коды) статический конструктор узла никогда пожаров; или, по крайней мере, не попадает в точку останова и не задает Node.Query ничего, кроме нуля.

Есть ли причина, почему это может произойти?

Решение

Заканчивать ответы ниже на несколько решений. Для моего случая я решил просто сделать переменную Query общедоступной и установить все экземпляры Query в одном месте. (Не идеально, но он работает.)

+0

Разве это де-сериализован через DataContractSerializer? Насколько я помню, DCS не запускает конструкторы. – PhillipH

+0

Почему бы просто не сделать запрос 'private const string Query = [query];'? – atlaste

+0

Это не так. Однако 'Load()' на самом деле является наследуемой статической функцией. Будет ли это иметь значение? –

ответ

4

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

Он вызывается автоматически перед созданием первого экземпляра или ссылки любые статические члены.

Вы можете предположить, что, если вы звоните

Node.Load(); 

что вы звоните статический метод на Node класса, но на самом деле вы вызываете его на базовом классе, так как это, где это .

Итак, чтобы исправить это, у вас есть два варианта. Во-первых, вы можете вызвать статический конструктор явно путем создания нового экземпляра класса Node до вызова Load()

var foo = new Node(); // static ctor triggered 
Node.Load(); 

или создать защищенный виртуальный элемент, базовый класс может позвонить, чтобы получить значение запроса (может 't использовать здесь абстракцию здесь, к сожалению)

public class DatabaseEntity<T> where T : Derp { 
    protected abstract string Query { get; } 
    public static IList<T> Load() {   
     return Database.Get(new DatabaseEntity<T>().Query); 
    } 
} 

Оба из них хакеры. Лучше вообще отказаться от статики и идти с помощью методов экземпляра. Статичность следует использовать экономно, так как они приводят к жесткой связи и другим головным болям, например.

4

Да, статические конструкторы не будут вызваны до тех пор, пока не будут доступны члены класса или не будет создан первый экземпляр.

В вашем случае вы получаете доступ к DatabaseEntity<T>.Load, поэтому статический конструктор DatabaseEntity<T> будет называться не его производными классами.

Несмотря на то, что вы звоните Node.Load, он отображается во время компиляции DatabaseEntity<Node>. Так что технически вы не получаете доступ к классу Node вообще.

+0

Это правильно, вы можете продемонстрируйте это поведение, создав экземпляр класса node перед вызовом Node.Load(), и он должен работать. Если экземпляр не выполняется до вызова, он не будет. –

+0

Это отличное объяснение. Можете ли вы предложить какие-либо подсказки или советы о том, как решить эту проблему? (Помимо очевидного, «Node n = new Node();') –

+2

@CharlesW Я подозреваю, что это проблема дизайна. Не зная больше о том, чего вы пытаетесь достичь, я не могу помочь с этим жаль. Очевидным обходным решением будет добавление метода «Node.Load» с новым модификатором, который просто вызывает реализацию базового класса, но. Я не рекомендую. –

Смежные вопросы