为什么我没有收到关于未初始化的只读字段的警告?

如果您忘记初始化私有或内部的只读成员,或者声明它的类是内部的,那么C#编译器就足以为您提供“永远不会分配字段”警告。 但是,如果该类是公开的,并且只读成员是公共的,受保护的或内部保护的,那么就没有警告!

有谁知道为什么?

示例代码,用于说明发出警告的条件以及未发出警告的条件:

namespace Test1 { class Test1 { #if TRY_IT public readonly int m; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 protected readonly int n; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 internal readonly int o; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 private readonly int p; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 protected internal readonly int q; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 Test1() { if( p != 0 ) //To avoid warning 'The field is never used' return; } #endif } public class Test2 { #if TRY_IT private readonly int m; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 internal readonly int n; //OK: warning CS0649: Field is never assigned to, and will always have its default value 0 Test2() { if( m != 0 ) //To avoid warning 'The field is never used' return; } #endif public readonly int o; //Blooper: no warning about field never assigned to. protected readonly int p; //Blooper: no warning about field never assigned to. protected internal readonly int q; //Blooper: no warning about field never assigned to. } public sealed class Test3 { public readonly int m; //Blooper: no warning about field never assigned to. } } 

编辑:您可能会认为编译器在公共成员和受保护成员的情况下不会发出警告,因为期望派生类可能初始化该字段是合理的。 由于多种原因,这个理论并没有任何用水:

  • 内部类可以是子类,但编译器在这种情况下不会发出警告。

  • 即使在密封类的情况下,编译器也无法发出警告,如示例代码中的Test3所示。

  • 无论派生类可能做什么或不做什么,该警告对于基类的完整性都是有意义的。

  • 语言明确禁止类初始化基类的只读成员。 (谢谢,Jim Mischel。)

编辑2:如果我的记忆力很好,Java会在所有情况下提供所有正确的警告,无论未初始化的最终成员是公共的,受保护的还是私有的,并且无论包含它的类是公共的还是仅在其包中可见。

简短的回答:这是编译器的疏忽。

更长的答案:启发式决定了为已声明和从未使用过的成员和本地人发出的警告,或者写入,从未阅读,阅读和从未写过的本地人,都不考虑该领域的只读权限。 正如您所记录的那样,它可以,从而在更多情况下发出警告。 我们可以说,未在任何ctor中初始化的公共只读字段“将始终具有其默认值”。

我会在新的一年里向Neal提及它,我们会看看我们是否可以在Roslyn中改进这些启发式方法。

顺便提一下,在许多情况下可以发出这种警告(不管是否为只读),但我们不这样做。 我今天不在办公室,所以我没有列出所有这些情况的方便,但足以说它们中有很多。 它就像“字段被声明为公共字段并且在内部类的公共嵌套类中”。 在那种情况下,该领域实际上是内部的,我们可以做警告,但有时我们不这样做。

很多年前的一天,我改变了启发式方法,以便每个可以静态知道未被使用的字段产生警告,并且当该更改进入我们用于编译写入的类库的C#编译器的内部版本时在C#中,一切都崩溃了。 这些家伙总是在编写“警告错误”的情况下开始编码,然后突然他们开始收到故意初始化或通过reflection和其他动态技术使用的各种字段的警告。 我以一种主要方式破坏了构建。 现在,有人可能会说,嘿,这些人应该修改他们的代码,以便它抑制警告(我确实认为),但最终结果是更容易将警告启发式支持到其先前的级别。 我应该逐渐做出改变。

这是MSDN文档:编译器警告(级别4)CS0649 :

字段“字段”永远不会分配给,并且始终具有其默认值“值”

编译器检测到未初始化的私有或内部字段声明,该声明从未赋值。

因此,对于非内部和非私人字段,您不应该期望有警告。

但我认为主要原因是C#编译器认为你应该初始化所有只能从你的程序集中访问的东西。 我猜C#编译器将其留给其他人来初始化其程序集中的非私有和非内部字段。

但我测试了protected internal ,我不知道为什么C#编译器不会对它发出警告。

如果它是公共的(或者更好的不是私有的),它可以被项目之外的另一个类使用。 这对于构建应该被其他人使用的库来说非常重要。 如果你会收到每个未使用的公共财产,字段或方法的警告,你就不会看到真正的问题。

我认为这是因为警告与范围和用法有关。 公共受保护成员和受保护成员可以在程序集范围之外和类使用者访问,开发人员可能实际上希望变量的值是其默认值。

另一方面,私有和内部成员在内部使用,并且您只是因为您不希望自己错误地更改它而只读它,因此它很可能应该在某处初始化。