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

C# 为什么我没有收到关于未初始化只读字段的警告?,c#,compiler-construction,C#,Compiler Construction,如果您忘记初始化私有或内部的只读成员,或者声明它的类是内部的,C#编译器会给您一个“field is never assigned to”警告。但是,如果类是public的,而只读成员是public、protected或protectedinternal的,那么就没有警告了 有人知道为什么吗 演示发出警告的条件和不发出警告的条件的示例代码: namespace Test1 { class Test1 { #if TRY_IT public readonly

如果您忘记初始化私有或内部的只读成员,或者声明它的类是内部的,C#编译器会给您一个“field is never assigned to”警告。但是,如果类是public的,而只读成员是public、protected或protectedinternal的,那么就没有警告了

有人知道为什么吗

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

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. 
    } 
} 
EDIT:有一段时间,您可能会认为编译器不会在公共成员和受保护成员的情况下发出警告,因为可以合理地预期派生类可能会初始化该字段。这一理论不成立的原因有很多:

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

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

  • 为了确保底座的完整性,该警告是有意义的 类,而不管派生类可能做什么或不做什么

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

EDIT2:如果我的内存很好,Java在所有情况下都会给出正确的警告,不管未初始化的最终成员是公共的、受保护的还是私有的,也不管包含它的类是公共的还是仅在其包中可见的。

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

这是:

字段“Field”从未分配给,并且始终具有其默认值 价值“价值”

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

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

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


但是我测试了受保护的内部,我不知道为什么C#编译器没有对此发出警告。

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


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

简而言之:这是编译器中的一个疏忽

较长的答案是:启发式方法决定向声明但从未使用、写入但从未读取、读取但从未写入的成员和本地人发出哪些警告,但没有考虑字段的只读性。正如您正确地注意到的,它可能会发出警告,从而在更多情况下发出警告。例如,我们可以说,未在任何ctor中初始化的公共只读字段“将始终具有其默认值”

在新的一年里,我会向Neal提到这一点,我们会看看我们是否能改进Roslyn的启发法

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


许多年前的一天,我改变了启发式方法,这样静态地知道每个未使用的字段都会产生一个警告,当这一改变成为我们用来编译用C编写的类库的C#编译器的内部版本时,所有的麻烦都爆发了。这些家伙总是在编译时打开“警告为错误”,突然,他们开始在所有类型的字段上收到警告,这些字段都是故意初始化的,或者是通过反射和其他动态技术仅使用的。我在很大程度上破坏了建筑。现在,有人可能会说,嘿,这些家伙应该修复他们的代码,这样它就可以抑制警告(我也这么认为),但最终证明,将警告启发式返回到以前的级别会更容易。我本应该更循序渐进地做出改变。

我不是在说未使用的成员;我说的是未初始化的只读成员。一个未初始化的只读成员对声明它的类以及世界上所有其他类实际上都是无用的。我知道。我在问为什么。@MikeNakis,我写下了我的理由,事实上你应该初始化那些可以从你的程序集中访问的字段。似乎这是一个好规则。哦,总是初始化所有字段,尤其是只读字段,这是一个完美的规则。这就是为什么如果你忘了做这件事,编译器总是会发出警告的原因。但在这里,编译器不使用+1来查找文档。@MikeNakis,在我看来,没有必要初始化可以在其他类或程序集中初始化的字段,事实上,在这种情况下,您不是创建者,因此您没有责任初始化它。所以没有警告