16

考虑以下 C# 代码:

using System;
class Program
{
    static string string1 = "AAA";
    static string string2 = string1 + string3;
    static string string3 = "BBB";

    static void Main()
    {
        Console.WriteLine(string2);
    }
}

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

两个问题:

  1. 为什么允许这样的代码编译成功?(如果答案是:“因为这就是 C# 规范的编写方式”,那么为什么要这样写?有什么原因我错过了为什么这并不总是只会引发编译时错误?)

  2. 如果我将来无意中再次编写此类代码,有什么方法可以获得编译时警告或其他类型的标志?

4

4 回答 4

6

如果您将静态初始化程序视为静态构造函数的逻辑部分,那么事情就更有意义了。就您而言,就好像您写过:

private static string String1;
private static string String2;
private static string String3;

static Program()
{
    String1 = "AAA";
    String2 = String1 + String3;
    String3 = "BBB";
}

静态初始化程序不能按您想要的方式工作的原因是,在一般情况下,编译器不可能重新排序。它可以在这种情况下,在许多其他情况下。但是,如果您考虑我在对类似问题的回答中提到的二阶效应,则编译器无法可靠地重新排序任何内容。

如果编译器“有时”重新排序,那将是极度混乱的。

于 2013-08-05T21:50:00.230 回答
6

对于问题 2:

像 ReSharper 这样的工具可以捕捉到这些东西。Visual Studio 中可能有一个设置,您可以打开它以获得更详细的编译输出。

在此处输入图像描述

这是 Reshaper 清理后产生“AAABBB”的代码

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

        }

    }

作为旁注,我想说,因为我们通常从上到下阅读,所以初始化以与 C# 规范 10.4.5.1 中所述相同的方式发生似乎是合乎逻辑的。

于 2013-08-05T20:37:56.717 回答
3

至于#1:允许代码编译的原因是,根据 C# 规范的 10.4.5.1,“类的静态字段变量初始化器对应于以文本顺序执行的一系列赋值它们出现在类声明中。” 这意味着您的string2变量已明确初始化为"AAA" + null. 有争议的是,您至少应该收到警告……至于为什么您的 IDE 选择不警告您,我不知道。

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

于 2013-08-05T20:23:03.150 回答
1

这是设计使然

类的静态字段变量初始化器对应于一系列赋值,它们按照它们在类声明中出现的文本顺序执行。

C# 有一个definite assignment策略,例如,字段是自动初始化的。因此,您string3会自动初始化为null,因此,就编译器而言,它已经具有价值。

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

于 2013-08-05T20:32:01.007 回答