【问题标题】:Constructor accessibility C# compiler error CS0122 vs CS1729构造函数可访问性 C# 编译器错误 CS0122 vs CS1729
【发布时间】:2026-01-27 05:30:01
【问题描述】:

①在以下C#代码中,出现CS1729,但我知道CS0122会更合适。

namespace A
{
  class Program
  {
    static void Main()
    {
      Test test = new Test(1);
    }
  }
  class Test
  {
    Test(int i) { }
  }
}

CS1729:“A.Test”不包含采用 1 个参数的构造函数

CS0122:'A.Test.Test(int) 由于其保护级别而无法访问'

②在以下C#代码中,出现CS0122,但我知道CS1729会更合适

namespace A
{
  class Program
  {
    static void Main()
    {
      Test test = new Test();
    }
  }
  class Test
  {
    Test(int i) { }
  }
}   

CS0122:'A.Test.Test(int) 由于其保护级别而无法访问'

CS1729:“A.Test”不包含采用 0 个参数的构造函数

问题:CS0122和CS1729在①和②中交换有什么原因还是这个C#编译器错误?

P.S.:①和②中的错误可以用 Microsoft Visual C# 2010 Compiler version 4.030319.1 重现。

【问题讨论】:

  • +1 我同意你的分析。有人会假设编译器会首先执行解析,然后检查可访问性。相反,它似乎首先过滤可访问性,然后检查匹配项。但是,出于性能原因,第二个可能更有意义(当然,完全取决于编译器内部)。
  • 公平地说,这两种错误消息都适用于这两种情况。首先发现哪个以及编译器何时停止报告可能取决于内部算法。要求编译器选择“最好的”错误(在人眼中)可能是一座过分的桥梁。
  • 只是猜测,但可能是因为 C# 默认会自动创建一个空构造函数,当您不传递参数时,编译器会假定使用了默认构造函数并首先检查可访问性。在第一个示例中,首先检查方法的解析可能被认为更有效,因为它不是类的默认行为。
  • @keyboardP 默认,当您显式声明任何其他构造函数时,编译器不会添加无参数。
  • @MarcinJuraszek - 确实,但我认为这可能是 when 编译器检查是否应添加默认值的情况。我对 C# 编译器了解不多,所以可能是一个疯狂的猜测(可能是:))

标签: c# constructor compiler-errors protection


【解决方案1】:

完全披露:我在 Microsoft 的 C# 团队工作。

来自编译器的诊断报告是一项棘手的工作!我们花费大量时间来确保针对特定错误情况报告“最佳”诊断。但是,这有时需要考虑启发式方法,而我们并不总是能做到这一点。在这种情况下,正如@Henrik Holterman 所指出的,这两个错误都是合理的(至少对于第二种情况)。

第一个示例显然是一个错误,尽管它的严重性较低。毕竟,它仍然是一个有点“正确”(我在这里有点客气)诊断的错误。在第二个示例中,两个错误都是正确的,但编译器未能选择“最佳”,希望是最有用的诊断。

借助 Roslyn C# 编译器,我们有机会重新审视我们的诊断报告并做出更好的选择。对于这些特定示例,Roslyn 编译器实际上会产生您所期望的错误。第一个例子上报CS0122,第二个例子上报CS1729。因此,您可以放心,这已经在未来的版本中得到修复。

【讨论】:

  • 如果你看到我的 cmets(从三天前)到这个问题,我不能同意两者都适用。例如,在我看来,仅仅因为过载是protected(比如说)就说过载不存在是不正确的,从技术上讲,我认为。同样,抱怨完全不相关的过载(用户特别要求另一个)的保护级别也不是“正确的”。但是,这可能仍然是最好的答案?
  • @Jeppe Stig Nielen -- 是的,你是对的。这显然是一个错误。我调整了我的回复以使其更清晰。
  • 在示例(2)中,重载A.Test.Test(int)(秘密存在并且)不可访问是正确的,但用户甚至没有要求该重载。它只是代码中的第一个重载(是的,我尝试了许多无法访问的重载)。
  • 是的,这是一个正确的分析。在这种情况下,根据过载解决方案的实施方式,可能会报告两种潜在的诊断,但 CS1729 对用户来说绝对是更好的一种。请注意,这在 IDE 体验中非常具有启发性。我们确保 Go to Definition 仍然会转到 A.Test.Test(int) 因为不清楚用户如何处理问题。他们是要删除参数并使构造函数公开,还是要在调用站点添加参数?好玩的东西。 :-)