访问基本成员时未调用子静态构造函数

时间:2014-12-15 15:20:37

标签: c# wpf

我有一个定义为:

的类
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的静态构造函数永远不会触发;或者至少没有遇到断点,也没有将Node.Query设置为null以外的任何东西。

有什么理由可能会发生这种情况吗?

解决方案

请查看以下答案,了解一些解决方案。对于我的情况,我决定公开Query变量,并在一个地方设置Query的所有实例。 (不理想,但它有效。)

3 个答案:

答案 0 :(得分:4)

是的,在首次访问类的成员或创建第一个实例之前,不会调用静态构造函数。

在您的情况下,您正在访问DatabaseEntity<T>.Load,因此将调用DatabaseEntity<T>的静态构造函数而不是其派生类。

即使您调用Node.Load,它也会在编译时映射到DatabaseEntity<Node>。从技术上讲,你根本不会访问Node课程。

答案 1 :(得分:4)

问题在于您对何时调用静态构造函数的假设。 documentation表示

,它不是最清楚的
  

在创建第一个实例或引用任何静态成员之前自动调用它。

如果你打电话

,你可能会认为
Node.Load();

您正在Node类上调用静态方法,但实际上您正在基类上调用它,因为这是实现它的地方。

所以,要解决这个问题,你有两个选择。首先,您可以通过在调用Load()之前创建Node类的新实例来显式触发静态构造函数

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

或者创建一个受保护的虚拟成员,基类可以调用以获取查询值(不幸的是,这里不能使用抽象)

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

两者都是hacky。最好完全放弃静态并使用实例方法。应谨慎使用静力学,因为它们会导致紧耦合和其他设计难题。

答案 2 :(得分:0)

您还可以通过执行以下操作直接使用System.Runtime.CompilerServicesRuntimeHelpers类型调用类构造函数: RuntimeHelpers.RunClassConstructor(type.TypeHandle);

例如,您可以使用反射在继承链中循环所有类型并调用每个静态构造函数。