【问题标题】:Why are Postfix ++/-- categorized as primary Operators in C#?为什么后缀 ++/-- 在 C# 中被归类为主要运算符?
【发布时间】:2011-10-26 08:03:05
【问题描述】:

目前,我正在向一班 C++ 程序员教授 C# 语言的基础知识。在我们讨论主题运算符时,我使用了 C# 标准类别的主要运算符、一元运算符等。

其中一位与会者感到困惑,因为在 C# 标准中,“后缀 ++/--”已被置于主要运算符的类别中,而不是“前缀 ++/--”。她造成这种混淆的理由是,她宁愿根据运算符“前缀 ++/--”来实现 C++ 运算符“后缀 ++/--”。换句话说,她宁愿将运算符“前缀++/--”算作主要运算符。 - 我明白她的意思,但我不能给她一个背后的理由。好吧,运算符“后缀 ++/--”的优先级高于“前缀 ++/--”,但这是唯一的理由吗?

规范在“14.2.1 运算符优先级和关联性”一节中提到了它。

所以我非常中性的问题是:为什么后缀 ++/-- 在 C# 中被归类为主要运算符? 有没有更深层次的道理?

【问题讨论】:

  • 即使是 C# 规范也没有在任何地方提到“主运算符”的类别......
  • 到目前为止,我还没有听说过“主要”和“次要”运算符之间的区别。您能否简单解释一下,或指出更多信息?
  • @BoltClock 和 stakx:参见 ECMA 论文中的“14.2.1 运算符优先级和关联性”。
  • 在 C# 中的运算符重载中,++ 运算符只返回值“x+1”[即要分配给变量的值,并用于表达式中前缀运算符的值]。其余语义由调用站点的编译器实现(通过引用或值类型分配)。这与 C++ 有很大不同,这与赋值运算符不能重载的原因相同。
  • 我认为这是一个武断的决定。如果要明确定义x+++y,其中一个必须比另一个具有更高的优先级。我认为乔恩的回答解决了问题中间令人困惑的部分,您在其中指的是 implementing 一个在另一个方面(这不能在 C# 中完成,只有一个运算符重载可用) .

标签: c# operators postfix-operator prefix-operator


【解决方案1】:

由于 ECMA 标准本身并没有定义“主”运算符是什么,除了优先顺序(即在“一元”之前)之外,没有其他意义。单词的选择可能很糟糕。

考虑到在许多 C-link 语言中,后缀运算符倾向于创建一个临时变量来存储表达式的中间结果(请参阅:"Prefer prefix operators over postfix" at Semicolon)。因此,它们与前缀版本有根本的不同。

尽管如此,快速检查 Mono 和 Visual Studio 如何使用后缀和前缀形式编译 for 循环,我发现生成的 IL 代码是相同的。仅当您使用后缀/前缀表达式的值时,它才会转换为不同的 IL(仅影响 'dup' 指令的放置位置),至少对于提到的那些实现。

【讨论】:

  • 嗯,奇怪的事情。 “如果你只需要副作用,则首选前缀而不是后缀”-规则对我来说是 C++ 程序员所熟知的(我也在 C#、Java 和 Objective-C 中使用该规则......你可以命名它)。在 C++ 中,优化通常只允许用于后缀运算符的内在类型,这就是“偏好规则”背后的基本原理。
【解决方案2】:

编辑:好的,现在我回家了,我已经删除了大部分令人困惑的部分......

我不知道为什么 x++ 被归类为主要表达式,而 ++x 不是;尽管我怀疑它在您编写的代码方面有很大的不同。同上优先。我想知道后缀是否被认为是主要的,因为它更常用?顺便说一句,在 ECMA 版本或 Microsoft C# 4 版本中,带注释的 C# 规范没有任何注释。 (我无法立即找到我的 C# 3 版本进行检查。)

但是,在实现方面,我认为++ 是一种伪运算符,前缀和后缀表达式都使用它。特别是,当您重载 ++ 运算符时,该重载用于后缀和前缀增量。正如 stakx 在评论中指出的那样,这与 C++ 不同。

需要注意的一点是,虽然后自增/后自减表达式必须将主表达式作为操作数,但前自增/预减表达式只需将一元表达式作为操作数。在这两种情况下,操作数都必须归类为变量、属性访问或索引器访问,所以我不确定有什么实际差异,如果有的话。

编辑:只是再发表一点评论,尽管它看起来很武断,但我同意当规范声明时它看起来确实很奇怪:

基本表达式包括最简单的表达式形式

但预增量的步骤列表比后增量的步骤列表更短/更简单(因为它不包括“保存值”步骤)。

【讨论】:

  • This C++ FAQ 可能与您的最后一条语句有关:一个 可以 分别重载 ++ 运算符的前缀和后缀形式。
  • ++x++ 并不比 ++1 更合法。 ++ 运算符返回一个值而不是变量/引用。因为你不能给一个值赋值(++ 运算符规范的一部分),所以这两个表达式都是没有意义的。
  • @Nico:是的,我仔细阅读了这个问题 - 但您的与会者在谈论 实现 一个运算符,我认为这不合适 -并且应该以与重载相同的术语来看待,其中有一个“增量”运算符可以由 either 后缀或前缀使用。 IMO 实施其中任何一个都是错误的。这就是为什么在回答您的问题时重载是相关的。
  • 乔恩,请让我们在这里停止讨论。我认为说“我无法回答,但我对你剩下的文字有话要说”是不对的。 - 可以评论,但不能回答!我将我的问题与正文的其余部分清楚地分开,只是想给出与会者在正文中混淆背后的 C++ 方面的基本原理。我不想阻止你(天哪!)!但我有权根据自己的看法采取行动,也没有人会阻止我这样做!
  • @Nico:你觉得不行,我觉得还可以……特别是,它说明了会如何回复那个与会者。如果您希望我的答案不存在(这通常是我在投票之前应用的测试),那很好。我真的认为作为评论会更好 - 它太长了,它解决了 IMO 问题的重要方面。但是,当然欢迎您不同意。我也很想听听其他投票者的意见......
【解决方案3】:

不同之处在于 a[i++] 将访问索引为 i 的元素。

a[++i] 将访问索引为 i+1 的元素。

在执行 a[++i/i++] 之后的两种情况

我将是 i+1。

这可能会带来麻烦,因为您无法对参数顺序做出假设

函数(i++,i++,i++)

将 i 增加 3 次,但您不知道顺序。如果最初我是 4 你也可以有 函数(4,5,6)

还有函数(6,5,4) 或函数(6,4,5)。

这仍然没什么,因为我使用了原生类型(例如“int”)作为示例,当你有类时情况会变得更糟。

重载操作符结果不改变时,改变的是它的优先级。这也可能造成麻烦。

因此,在一种情况下,“++”在返回引用之前应用,在另一种情况下,在返回引用之后应用。当你重载时,在返回引用之前应用它可能会更好(所以 ++something 比 something++ 好得多,至少从重载的角度来看。)

采用重载 ++ 的泛型类(我们有 2 个项目,foo 和 bar)

    foo = bar ++; //is like writing (foo=bar).operator++();

    foo = ++bar; // is like writing foo= (bar.operator++());

而且有很大的不同。尤其是当您只是不分配引用而是使用它做一些更复杂的事情时,或者在您的对象内部有一些与浅拷贝和深拷贝有关的东西。

【讨论】:

  • 本题大部分读者都知道表达式++i和i++的区别,但这不是本题的本质。两个运算符有不同的优先级,我只想知道,这是否是 only 将它们放在不同类别中的原因。您的某些陈述正确:
  • - 实际上执行顺序在C#中严格定义为l->r(与C++相反)。因此,如果 i = 0 是 int,则在 C# 中调用以下函数: function(i++, i++, i++);将总是导致这个执行:function(i, i+1, i+2);
  • - 在 C# 和 C++ 中,程序员都不能改变运算符的优先级,也许你想表达一些不同的东西。您的某些陈述是正确的,但与问题无关。
  • 事实上,这是考虑 C# 和 C++ 之间差异的好方法,并且在设计语言时考虑了其中的原因。只是尝试超越 SPEC。希望有更哲学的评论:) 这仍然与问题有关,因为您的班级是 C++ 学生,不是吗?这是回到 C++ 的一步。问候。
  • 我明白了!根据您的评论,您的答案内容是有意义的(+1)。您会以某种方式将您的评论编入答案吗? - 所以其他读者可以跟随你的论点。根据哲学:请清楚地标记哪种语言存在哪些功能(C ++中未定义的执行顺序,优先级是不可变的等)。问候
猜你喜欢
  • 1970-01-01
  • 2020-08-09
  • 2011-08-30
  • 2017-01-23
  • 2014-12-03
  • 2011-11-16
  • 1970-01-01
  • 2022-11-24
  • 1970-01-01
相关资源
最近更新 更多