C# 如何捕获或标记静态字段初始化顺序引起的潜在问题

C# 如何捕获或标记静态字段初始化顺序引起的潜在问题,c#,visual-studio-2010,C#,Visual Studio 2010,考虑以下C#代码: 今天早些时候,我编写了一些类似这样的代码,并希望string2包含值AAABBB,但它只包含AAA。我读了一些关于静态变量初始化顺序的书,但对我来说似乎更可取的是,在编译过程中会生成某种类型的警告或错误 两个问题: 为什么允许这样的代码成功编译?(如果答案是:“因为C#spec就是这样写的”,那么为什么它是这样写的?有什么原因我不知道为什么它不总是抛出编译时错误吗?) 如果我以后无意中再次编写此类代码,有没有办法得到编译时警告或其他类型的标志 至于#1:允许代码编译的原因是,

考虑以下C#代码:

今天早些时候,我编写了一些类似这样的代码,并希望
string2
包含值
AAABBB
,但它只包含
AAA
。我读了一些关于静态变量初始化顺序的书,但对我来说似乎更可取的是,在编译过程中会生成某种类型的警告或错误

两个问题:

  • 为什么允许这样的代码成功编译?(如果答案是:“因为C#spec就是这样写的”,那么为什么它是这样写的?有什么原因我不知道为什么它不总是抛出编译时错误吗?)

  • 如果我以后无意中再次编写此类代码,有没有办法得到编译时警告或其他类型的标志

  • 至于#1:允许代码编译的原因是,根据C#规范的10.4.5.1,“类的静态字段变量初始值设定项对应于一系列赋值,这些赋值按文本顺序执行,在类声明中以文本顺序出现。”这意味着您的
    string2
    变量被明确地初始化为
    “AAA”+null
    。你至少应该得到一个警告,这是有争议的。。。至于你的IDE为什么不警告你,我不知道

    关于#2:我不知道。这似乎是一个更合适的问题,如果同时使用IDE标记的话

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

    C#有一个
    明确赋值
    策略,这意味着,例如,字段会自动初始化。因此,您的
    string3
    会自动初始化为
    null
    ,因此,就编译器而言,它已经有了值

    这意味着
    string2
    的输出是
    string1+null
    (这只是
    string2
    ),因此编译器没有理由抛出任何错误(尽管我确实看到了警告的作用)。

    对于问题2:

    像ReSharper这样的工具可以捕捉这些东西。VisualStudio内部可能有一个设置,您可以打开该设置以获得更详细的编译输出

    以下是整形器清理后生成“AAABBB”的代码


    作为旁注,我要说的是,由于我们通常从上到下阅读,初始化的方式与C#规范10.4.5.1中描述的方式相同似乎是合乎逻辑的。

    如果您认为静态初始值设定项在逻辑上是静态构造函数的一部分,那么事情就更有意义了。就你而言,就好像你写过:

    private static string String1;
    private static string String2;
    private static string String3;
    
    static Program()
    {
        String1 = "AAA";
        String2 = String1 + String3;
        String3 = "BBB";
    }
    
    静态初始值设定项不能按您想要的方式工作的原因是,在一般情况下,编译器不可能重新排序。在这种情况下,以及在许多其他情况下,它都可能发生。但是如果你考虑我在我提到的二阶效应,编译器就不能可靠地重新排序任何东西。
    如果编译器“有时”进行重新排序,那将非常混乱。

    *me认为*实际上不可能导致
    “AAA”
    。。。复制/粘贴,运行…哦,哇,真的!当我投票时,头脑发昏,希望Skeet或Lippert会回答这个问题。如果这是一个问题,不要在声明时初始化,而是在执行赋值的地方运行静态构造函数。ReSharper对此给出了警告。@spender如果我知道的话,这不是问题。问题是,如果我无意中编写了此类代码,并且没有任何东西警告/标记我,并且相关的引用:17.4.5:在默认值状态下,可能会观察到带有变量初始值设定项的静态字段。但是,从风格的角度来看,这是非常不鼓励的。我想知道Visual Studio是否确实有一个设置来捕捉/标记这一点,因为我工作的地方没有R#。我同意从上到下进行初始化的“逻辑性”。但是,如果这些字段不是静态的,编译器就会抛出一个错误。对我来说,这似乎更符合逻辑。@RSW-是的,但要得到这种推理,C#设计团队的人必须回答:)我刚刚通过VS2012 Premium进行了代码分析,但没有任何警告。谢谢你的回答。它确实提供了对事物如何运作的洞察。然而,我好奇的是,为什么编译器允许这样的场景溜走。为什么它不至少用一个警告来标记呢?是否有需要这种灵活性的用例,以至于编译器乐于让它们溜走?在我看来,由于C#spec强烈反对这种用法,大多数在野外使用这种行为都是无意的,就像我的情况一样。@RSW:Eric Lippert,以前是C#开发团队的成员,在博客中谈到了警告的利弊。我怀疑编译器团队认为这还不足以证明警告的合理性。请参阅,了解一些相关的案例。Lippert还链接到解释用于决定什么应该产生警告的决策过程。我提议的场景通过了他的前四个标准,但在后两个标准上可能失败,即:(5)“另一个工具能产生警告吗?”,在这种情况下,R#能,和(6)“用户在测试后会立即发现错误吗?”,这是有争议的。我想知道C#团队是否真的考虑过这个问题并拒绝了它。如果
    string1
    string2
    、和
    string3
    不是静态的,编译器根本不会让这个代码编译。但由于它们是静态的,所以它可以毫无问题地运行。如果是故意的,那么我的问题是“为什么会有不同”?原因是
    class Program
        {
            private const string String1 = "AAA";
            private const string String2 = String1 + String3;
            private const string String3 = "BBB";
    
            static void Main()
            {
                Console.WriteLine(String2);
                Console.ReadLine();
    
            }
    
        }
    
    private static string String1;
    private static string String2;
    private static string String3;
    
    static Program()
    {
        String1 = "AAA";
        String2 = String1 + String3;
        String3 = "BBB";
    }