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

时间:2011-12-31 12:57:45

标签: c# compiler-construction

如果您忘记初始化私有或内部的只读成员,或者声明它的类是内部的,那么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. 
    } 
} 

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

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

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

  • 为了基础的完整性,警告是有意义的 类,无论派生类可能做什么或不做什么。

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

EDIT2:如果我的记忆很好,Java会在所有情况下提供所有正确的警告,无论未初始化的最终成员是公共的,受保护的还是私有的,并且无论该类是否包含它只是在包装内公开或可见。

4 个答案:

答案 0 :(得分:24)

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

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

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

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

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

答案 1 :(得分:4)

这是MSDN Documentation: Compiler Warning (level 4) CS0649

  

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

     

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

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

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

但是我测试了protected internal,我不知道为什么C#编译器没有警告它。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

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