14

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

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

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

所以我非常中性的问题:为什么后缀 ++/-- 在 C# 中被归类为主要运算符?这里面有更深的真相吗?

4

3 回答 3

2

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

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

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

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

编辑:只是再发表一点评论,即使它看起来是任意的,我同意当规范声明时它看起来确实很奇怪:

主要表达式包括最简单的表达式形式

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

于 2011-08-13T08:37:21.220 回答
2

由于ECMA 标准本身并没有定义什么是'Primary' 运算符,所以除了优先顺序(即在'Unary' 之前)之外,没有其他意义。措辞的选择可能很糟糕。

考虑到在许多 C-link 语言中,后缀运算符倾向于创建一个临时变量来存储表达式的中间结果(请参阅:分号中的“优先使用前缀运算符而不是后缀”)。因此,它们与前缀版本根本不同。

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

于 2011-08-13T13:19:09.747 回答
1

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

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

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

我将是 i+1。

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

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

将 i 增加 3 次,但您不知道顺序。如果最初 i 是 4,你也可以拥有 function(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++());

并且有很大的不同。尤其是当您只是不分配引用而是对其进行更复杂的操作时,或者在内部您的对象具有与浅拷贝 VS 深拷贝有关的东西。

于 2012-11-16T14:59:51.477 回答