为什么C#中不允许使用const参数?

Inc*_*ito 98 c# language-agnostic language-design

对于C++开发人员来说,它看起来很奇怪 在C++中,我们用来标记参数,const以确保它的状态不会在方法中改变.还有其他C++特定的原因,比如const ref为了传递ref而传递,并确保状态不会被更改.但是为什么我们不能在C#中标记为方法参数const?

为什么我不能像下面那样声明我的方法?

    ....
    static void TestMethod1(const MyClass val)
    {}
    ....
    static void TestMethod2(const int val)
    {}
    ....
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 58

除了其他好的答案之外,我还要补充另一个原因,就是不要将C风格的常量放入C#中.你说:

我们将参数标记为const,以确保其状态不会在方法中更改.

如果const实际上这样做,那将是伟大的.Const不这样做.常数是谎言!

Const不保证我可以实际使用.假设您有一个采用const事物的方法.有两个代码作者:编写调用者的人和编写被调用的人.被调用者的作者使该方法采用了const.这两位作者可以假设对象是不变的吗?

没有.被调用者可以自由地抛弃const并改变对象,因此调用者无法保证调用带有const的方法实际上不会改变它.类似地,被调用者不能假设对象的内容在被调用者的整个动作中都不会改变; 被调用者可以在const对象的非常量别名上调用一些变异方法,现在所谓的const对象已经改变了.

C风格的const不保证对象不会改变,因此被破坏.现在,C已经有了一个弱类型系统,你可以在这个系统中重新解释一个double转换为一个int,如果你真的想要的话,那么它对于const也有一个弱类型系统也就不足为奇了.但是C#被设计成一个好的类型系统,一个类型系统,当你说"这个变量包含一个字符串"时,变量实际上包含对字符串的引用(或null).我们绝对不希望将C风格的"const"修饰符放入类型系统中,因为我们不希望类型系统成为谎言.我们希望类型系统很强大,以便您可以正确地推理您的代码.

C中的Const是一个指导原则 ; 它基本上意味着"你可以相信我不要试图改变这件事".那不应该是类型系统 ; 类型系统中的东西应该是关于你可以推理的对象的事实,而不是它的使用指南.

现在,不要误会我的意思; 仅仅因为C中的const被深深打破并不意味着整个概念是无用的.我希望看到的是C#中一些实际上正确有用的"const"注释形式,这是人类和编译器可以用来帮助他们理解代码的注释,运行时可以用来做自动并行化和其他高级优化.

例如,想象一下,如果你可以围绕一大堆代码"画一个盒子"并说"我保证这段代码不会对这个类的任何字段执行任何突变",这种方式可以由编译器检查.或者画一个框,上面写着"这个纯粹的方法会改变对象的内部状态,但不会以任何方式在框外观察".这样的对象不能自动安全地多线程,但可以自动记忆.我们可以在代码上添加各种有趣的注释,以实现丰富的优化和更深入的理解.我们可以比弱C风格的const注释做得更好.

但是,我强调这只是猜测.我们没有确定的计划将此类功能纳入任何假设的未来版本的C#,如果有的话,我们还没有宣布这种方式.这是我希望看到的东西,以及即将强调多核计算可能需要的东西,但这些都不应被解释为对C#的任何特定特征或未来方向的预测或保证.

现在,如果您想要的仅仅是局部变量的注释,这是一个参数,表示"此参数的值在整个方法中不会改变",那么,当然,这很容易完成.我们可以支持一次初始化的"readonly"本地和参数,以及在方法中改变的编译时错误."using"语句声明的变量已经是一个本地变量; 我们可以为所有本地和参数添加一个可选的注释,使它们像"使用"变量一样.它从未成为一个非常高优先级的功能,因此从未实现过.

  • _"被叫者可以自由地抛弃常数......"_嗯嗯......(°_°)这是你在这里做的一个非常浅薄的论点.被调用者也可以自由地开始用`0xDEADBEEF`编写随机存储器位置.但两者都是***非常糟糕的***编程,并且被任何现代编译器早期和尖锐地捕获._在将来编写答案时,请不要混淆技术上可能使用语言的后门与实际现实代码中可行的内容.像这样的倾斜信息会使经验不足的读者感到困惑,并降低SO上的信息质量. (48认同)
  • 对不起,但我发现这个论点很弱,开发人员可以说谎的事实无法用任何语言来阻止.修改对象a的void foo(const A&a)与返回梨数的int countApples(Basket b)没有什么不同.这只是一个bug,一个用任何语言编译的bug. (30认同)
  • 这个答案是我有时讨厌C#设计的原因.语言设计者绝对肯定他们比其他开发人员更聪明,并试图过度保护他们免受错误.很明显,const关键字只能在编译时工作,因为在C++中我们可以直接访问内存并可以执行任何我们想要的操作,并且有多种方法可以获得循环const声明.但在正常情况下没有人这样做.顺便说一句,C#中的'private'和'protected'也不能保证,因为我们可以使用反射来访问这些方法或字段. (26认同)
  • @BlackOverlord:(1)设计一种语言,其特性自然导致开发人员成功而不是失败的愿望是由于*建立有用工具*的愿望,而不是相信设计师比他们的客户更聪明,就像你猜想的那样.(2)反射不是C#语言的一部分; 它是运行时的一部分; 对反射的访问受运行时安全系统的限制.低信任代码未被授予进行私有反射的能力.访问修饰符提供的*保证*用于*低信任代码*; 高信任代码可以做任何事情. (10认同)
  • _"C中的Const是一个指导原则;"_引用您所说的一些内容的来源将真正有助于您的案例.在我的教育和行业经验中,引用的声明是hogwash-C在C中是与类型系统本身一样的强制内置功能 - 两者都有后门在必要时欺骗它们(就像C中的所有内容一样)但是预期本书在99.99%的代码中使用. (8认同)
  • C++的`const`并没有"深受打击".经常被误解?是的(遗憾的是,通过你指出的非const别名进行修改的可能性仍然让很多C++程序员感到惊讶).没有比保证引用对象不会改变更有用的东西?是的,很清楚.const不能消除每个bug,但这不是放弃它的理由. (5认同)
  • @ SlippD.Thompson:首先,我注意到一个功能,它提供了"必要时作弊的后门",开发人员不会使用它正是我所说的; C中的const实际上并没有提供我想要的语言保证.其次,我反对你的广告诽谤.第三,问题是"为什么不呢?" 一个本质上需要意见的问题 - 我的意见是我与C#设计委员会的长期合作.最后,如果你不喜欢我的话,我鼓励你写一个更好的答案,或写一篇博文或其他类似的东西. (4认同)
  • 答案是基于对 C++ const 参考模式的完全误解,并且违背了正确的软件工程。Constness 是 C++ 中非常强大的工具,因为它明确地向开发人员显示函数对参数传递的对象做了什么或不做了什么。确实,正确使用它并不总是那么容易,但它是 C++ 中最广泛使用的模式之一。它可以保护开发人员避免犯多线程错误,有助于理解数据流,提高可读性,并使代码更易于扩展。不,**没有人从 const 转换为非 const**。 (4认同)
  • C ++中的const是对意图的有效且有目的的保证。您关于说谎的程序员的示例有些牵强。当然可以做得更好。但是,您过去曾写过一些非常好的文章。因此,您能否将答案扩展到“如果您的生活取决于此功能,如何在C#中添加const正确性”?还是你宁愿死? (3认同)
  • 对于你反对BlackOverlord关于反思水的观点的反驳,你不得不争辩说所有严肃的C#开发都是低信任的代码.(C#的"保证"隐私的事实被语言本身以外的东西打破了,而C++对const的"保证"在语言中被`const_cast <T>()`打破了,这与程序员的观点完全不同.观点:在一天结束时,"保证"仍然被打破.) (3认同)
  • 你是否因为有人对你撒了谎而花了好几天的时间来追踪 ac 或 c++ 程序中的错误,或者这些无用的咆哮从何而来?并不是说如果可以的话我不会禁止 `const_cast` 并且我确实看到了 `mutable` 的危险,但是你所说的任何内容都没有给出任何理由说明为什么这个概念没有在 C# 中(正确地)实现(除了最后的可能:* “它从来都不是一个非常高优先级的功能”*)。 (2认同)
  • 这个答案详细说明了一些关于常数的常见误解.Constness是对作者和界面用户之间*契约*的规定,因此任何一方都不会意外地违反合同.您不能在现代C++编译器中意外抛弃constness - 您必须站在您的头上,使用特定的语法.任何有权访问代码的人都可以搜索此类强制转换的任何案例.正确应用constness可以消除很难找到的错误,并简化编程.(这来自一个做过很多C++,Java和C#的人) (2认同)
  • @AF_cpp 是的,确实如此。抱歉,这个答案很糟糕,不敢相信它有这么多的赞成票。你所说的关于C的说法是完全错误的。这个答案包含太多与事实相反的观点,不适合SO。 (2认同)
  • @EricLippert这不是我的观点,从C的角度来看你的答案是错误的。我知道微软总体上不喜欢C,那好吧,但这不允许你写出关于C的错误内容。批评你的答案与我自己的答案没有任何联系。你只是说“如果你不高兴就走开”,这是一种非常错误的行为,我只是发表评论来解释为什么我投了反对票。另外,我看到 C# 7.2 现在可能对此感兴趣,https://learn.microsoft.com/fr-fr/dotnet/csharp/write-safe-efficient-code。 (2认同)
  • 我没有写错什么,我也没有鼓励你走开。我正在做相反的事情:我鼓励您撰写教育他人的帖子,以便您的知识可以帮助我们所有人。我很欣赏 C# 新功能的链接,但我已经知道它们了。它们通过观察八年前我在写这个答案时发现的 C 设计问题来说明如何正确有效地设计常量正确性,当时我们没有计划向 C# 添加这样的功能。 (2认同)
  • 然后,您可能会考虑做的事情就是写出此问题的答案,以说明新的C#7.2功能如何解决C中const功能的缺点,从而回答所提出的问题。不幸的是,我自己没有时间这样做,但是很明显,您对这个主题有强烈的兴趣,因此,我鼓励您花一些时间将您的想法扩展为有用的答案。 (2认同)

Rub*_*ben 24

C#中没有const正确性的原因之一是因为它在运行时级别不存在.请记住,除非它是运行时的一部分,否则C#1.0没有任何功能.

CLR没有const正确概念的几个原因是例如:

  1. 它使运行时复杂化; 此外,JVM也没有它,并且CLR基本上是作为一个项目来创建类似JVM的运行时,而不是类似C++的运行时.
  2. 如果在运行时级别存在const正确性,那么BCL中应该有const正确性,否则就.NET Framework而言,该功能几乎没有意义.
  3. 但是如果BCL需要const正确性,那么CLR之上的每种语言都应该支持const正确性(VB,JavaScript,Python,Ruby,F#等).这种情况不会发生.

Const正确性几乎是C++中仅存在的语言特性.因此,它几乎归结为同样的论证,即为什么CLR不需要检查异常(这是一种仅Java语言的特性).

另外,我认为您不能在托管环境中引入这种基本类型系统功能而不会破坏向后兼容性.因此,不要指望进入C#世界的const正确性.

  • 决赛并不是真的.您仍然可以更改参考IIRC上的字段,而不是值/引用本身.(无论如何都是通过值传递的,所以它更像是一个编译器技巧,可以防止你更改参数值.) (2认同)
  • 它强制执行_inside_方法,因此在BCL中引入它不会是一个重大变化. (2认同)

Ste*_*ary 12

我相信C#不是const-correct有两个原因.

首先是可理解性.很少有C++程序员理解const正确性.简单的例子const int arg是可爱的,但我也看到了char * const * const arg- 一个常量指针指向非常量字符的常量指针.指向函数的指针的const-correctness是一个全新的混淆程度.

第二个是因为类参数是通过值传递的引用.这意味着已经有两个级别的常量要处理,没有明显清晰的语法.类似的绊脚石是集合(以及集合的集合等).

正确性是C++类型系统的重要组成部分.从理论上讲,它可以添加到C#中,只能在编译时检查(它不需要添加到CLR中,除非包含const成员方法的概念,否则不会影响BCL) .

但是,我认为这不太可能:第二个原因(语法)很难解决,这将使第一个原因(可理解性)成为一个问题.

  • 无法理解常量性的程序员不应该使用 C++ 进行编程。 (2认同)

isg*_*oed 9

从 C# 7.2(2017 年 12 月,Visual Studio 2017 15.5)开始,这是可能的。

适用于结构和基本类型, 不适用于类的成员。

您必须使用in来通过引用将参数作为输入发送。看:

请参阅:https : //docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-parameter-modifier

对于您的示例:

....
static void TestMethod1(in MyStruct val)
{
    val = new MyStruct;  // Error CS8331 Cannot assign to variable 'in MyStruct' because it is a readonly variable
    val.member = 0;  // Error CS8332 Cannot assign to a member of variable 'MyStruct' because it is a readonly variable
}
....
static void TestMethod2(in int val)
{
    val = 0;  // Error CS8331 Cannot assign to variable 'in int' because it is a readonly variable
}
....
Run Code Online (Sandbox Code Playgroud)


Pav*_*aev 5

const在C#中表示"编译时常量",而不是像C++中那样"只读"但可能是其他代码可变的".constC#中C++的粗略模拟是readonly,但是那个只适用于字段.除此之外,没有C++ - 就像C#中的const正确概念一样.

理由很难说清楚,因为有很多潜在的原因,但我的想法是希望首先保持语言的简单性,以及实现这一点的不确定优势.