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

Why do I NOT get warnings about uninitialized readonly fields?

如果忘记初始化私有或内部的只读成员,或者声明该成员的类是内部的,则C编译器足以为您提供"字段从未分配给"警告。但是,如果类是公共的,只读成员是公共的、受保护的或受内部保护的,那么就没有警告了!

有人知道为什么吗?

示例代码,演示发出警告的条件和未发出警告的条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
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所示。

  • 为了基地的完整性,警告是有意义的。不管派生类可以做什么,也可以不做什么。

  • 语言明确禁止类初始化基类的只读成员。(谢谢,吉姆·米舍尔。)

Edt2:如果我的记忆力很好,Java在所有情况下都给出了所有正确的警告,不管未初始化的最终成员是公共的、受保护的还是私有的,而不管包含它的类是否在其包内是公共的或可见的。


简短的回答:这是编译器中的一个监督。

较长的答案是:启发式的,它决定了对声明和从未使用过的成员和本地人发出什么警告,或从未读过、读过和从未写过的警告,不考虑字段的只读性。正如您正确地注意到的,它可以,从而在更多情况下发出警告。例如,我们可以说,未在任何ctor中初始化的公共只读字段"将始终具有其默认值"。

我会在新的一年里把它告诉尼尔,我们会看看能否在罗斯林改进这些启发式方法。

顺便说一句,在许多情况下,可以发出此类警告(不考虑只读性),但我们不这样做。我今天不在办公室,所以我手头上没有所有这些情况的清单,但足以说明其中有很多情况。它类似于"字段声明为公共字段,并且位于内部类的公共嵌套类中"。在这种情况下,该领域实际上是内部的,我们可以发出警告,但有时我们不能。

许多年前的一天,我改变了启发式方法,使每个静态已知未使用的字段都产生了一个警告,当这个改变使它成为C编译器的内部版本时,我们用来编译用C编写的类库,所有的事情都变得一团糟。这些人总是在编译时打开"warnings as errors",突然,他们开始在所有类型的字段上收到警告,这些字段是故意初始化的,或者仅通过反射和其他动态技术使用的。我以一种主要的方式打破了这个建筑。现在,有人可能会说,嘿,这些人应该修复他们的代码,这样它就可以抑制警告(我也这么认为),但最终证明,将警告启发式恢复到以前的水平更容易。我应该让变化更为缓慢。


这是msdn文档:编译器警告(4级)cs0649:

Field 'field' is never assigned to, and will always have its default
value 'value'

The compiler detected an uninitialized private or internal field
declaration that is never assigned a value.

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

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

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


如果它是公共的(或者最好不是私有的),它可以被项目之外的其他类使用。这对于构建应该由他人使用的库很重要。如果每一个未使用的公共属性、字段或方法都会得到警告,那么您就不会看到真正的问题。


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

另一方面,私有成员和内部成员在内部使用,您将其设置为只读,因为您不希望自己错误地更改它,所以很可能应该在某个地方对其进行初始化。