C# 实例构造函数之前未运行静态字段初始值设定项

C# 实例构造函数之前未运行静态字段初始值设定项,c#,C#,我有以下课程: public class AssignmentStatusCode { public static AssignmentStatusCode Pending { get; } = new AssignmentStatusCode("P"); public static AssignmentStatusCode Rejected { get; } = new AssignmentStatusCode("R"); public static Assig

我有以下课程:

public class AssignmentStatusCode 
{

    public static AssignmentStatusCode Pending { get; } = new AssignmentStatusCode("P");

    public static AssignmentStatusCode Rejected { get; } = new AssignmentStatusCode("R");

    public static AssignmentStatusCode Approved { get; } = new AssignmentStatusCode("A");


    public static implicit operator string(AssignmentStatusCode assignmentStatusCode)
    {
        return assignmentStatusCode.Value;
    }

    private static readonly HashSet<string> ValidStatusCodes = new HashSet<string>(new[] { "A", "R", "P" });

    public AssignmentStatusCode(string value)
    {
        if (!ValidStatusCodes.Contains(value))
        {
            throw new ArgumentOutOfRangeException(nameof(value),
                                                  $"Value must be {string.Join(", ", ValidStatusCodes.Select(c => $"'{c}'"))}.");
        }

        Value = value;
    }

    public string Value { get; }
}
公共类分配StatusCode
{
公共静态AssignmentStatusCode挂起{get;}=新的AssignmentStatusCode(“P”);
公共静态AssignmentStatusCode被拒绝{get;}=new AssignmentStatusCode(“R”);
公共静态AssignmentStatusCode Approved{get;}=新的AssignmentStatusCode(“A”);
公共静态隐式运算符字符串(AssignmentStatusCode AssignmentStatusCode)
{
返回assignmentStatusCode.Value;
}
私有静态只读哈希集ValidStatusCodes=新哈希集(新[]{“A”、“R”、“P”});
公共分配状态代码(字符串值)
{
如果(!ValidStatusCodes.Contains(值))
{
抛出新ArgumentOutOfRangeException(nameof(value)),
$“值必须是{string.Join(“,”,ValidStatusCodes.Select(c=>$”{c}'));
}
价值=价值;
}
公共字符串值{get;}
}
当我使用
var a=new AssignmentStatusCode(“a”)
创建此类的实例时,实例构造函数的
if
检查会抛出
NullReferenceException
。调试表明
ValidStatusCodes
null

ValidStatusCodes
上有一个静态初始值设定项

根据C#规范:

如果类中存在静态构造函数(§10.12), 静态字段初始值设定项的执行发生在 执行静态构造函数否则,静态字段 初始值设定项在执行之前在依赖于实现的时间执行 该类静态字段的首次使用

为什么静态字段在构造函数中被访问之前没有被初始化?我有一种感觉,有一件非常简单的事情我正在掩饰,但我花了太多时间调试,没有任何进展;是时候寻求帮助了


显然,如果我仔细阅读规范,我会在上面引用的段落开头注意到这一点,这是我问题的根源

10.5.5.1静态字段初始化类的静态字段变量初始值设定项对应于一系列赋值,这些赋值 按照它们在类中出现的文本顺序执行 声明。如果类中存在静态构造函数(§10.12), 静态字段初始值设定项的执行发生在 执行静态构造函数。否则,静态场 初始值设定项在执行之前在依赖于实现的时间执行 该类静态字段的首次使用


谢谢大家的帮助。

我想问题是在
public static AssignmentStatusCode Pending{get;}=new AssignmentStatusCode(“p”)您的代码包含实例构造函数的调用,该调用在哈希集初始化之前执行

我认为您收到的异常的stacktrace应该显示构造函数的调用thah引发的错误就是我们指出的那个


正如注释中所建议的,您应该移动
私有静态只读HashSet ValidStatusCodes=newhashset(new[]{“a”、“R”、“p”})在构造函数调用的行之前。

静态字段和属性按照它们在类中出现的顺序进行初始化

引述:

类的静态字段变量初始值设定项对应于按文本顺序执行的赋值序列,在类声明中以文本顺序出现

此外,为了完整性:

如果类中存在静态构造函数,则在执行该静态构造函数之前立即执行静态字段初始值设定项。否则,静态字段初始值设定项将在第一次使用该类的静态字段之前的依赖于实现的时间执行

有人在评论中正确地指出了这一点,所以这里没有提到属性,只提到字段。我想说,auto属性是私有字段和带有get和set访问器的属性的语法糖,而get()和set(value)方法更为糖。因此,上述内容确实适用于属性,唯一需要注意的是,我不知道编译器在哪里以及如何对这些支持字段进行排序

您可以使用依赖于初始化字段之后的字段的ctor初始化挂起、拒绝和接受字段


或者将hashset字段放在第一位,这样它就可以首先初始化。或者我认为更好的方法是使用一个静态ctor来初始化所有这些,这样您就可以清楚地看到每个ctor的顺序和依赖关系。还参考前面关于自动属性和编译器存储私有备份字段的说明,使用静态ctor更为恰当,并且完全相信它们获得设置在它们上的适当值的顺序

当根据排序顺序实例化类时,将初始化属性。**ValidStatusCodes**应该在你的班级中名列前茅,出现此错误的原因是,您在初始化ValidStatusCodes属性之前调用了构造函数。

Move
private Status readonly HashSet ValidStatusCodes…
before
public Status Assignment StatusCode Pending…
并且应该可以工作。有关Alessandro的答案为何可以工作的解释,请参阅C#规范(ECMA-334)17.11:“如果一个类包含任何带有初始值设定项的静态字段,那么在执行静态构造函数之前,这些初始值设定项将按照文本顺序执行。”啊,谢谢。我很久以前写过这个类,但忘记了静态属性也要通过构造函数。当我今天早上添加静电场时,我没有