简体   繁体   English

为什么后缀 ++/— 在 C# 中被归类为主要运算符?

[英]Why are Postfix ++/— categorized as primary Operators in C#?

Currently I'm teaching a class of C++ programmers the basics of the C# language.目前我正在向 C++ 程序员的 class 教授 C# 语言的基础知识。 As we discussed the topic operators I used C# standard categories of primary, unary etc. operators.在我们讨论主题运算符时,我使用了 C# 标准类别的主要、一元等运算符。

One of the attendees felt puzzled, because in the C# standard the "postfix ++/--" have been put in the category of primary operators rather than the "prefix ++/--".其中一位与会者感到困惑,因为在 C# 标准中,“后缀 ++/--”已被置于初级运算符的类别中,而不是“前缀 ++/--”。 Her rationale behind this confusion was, that she would rather implement the C++ operator "postfix ++/--" in terms of the operator "prefix ++/--".这种混淆背后的原因是,她宁愿根据运算符“前缀 ++/--”来实现 C++ 运算符“后缀 ++/--”。 In other words she would rather count the operator "prefix ++/--" as a primary operator.换句话说,她宁愿将运算符“前缀++/--”算作主要运算符。 - I understand her point, but I can't give to her a rationale behind that. - 我理解她的观点,但我不能给她一个背后的理由。 OK the operators "postfix ++/--" have a higher precedence than "prefix ++/--", but is this the only rationale behind that?好的,运算符“postfix ++/--”的优先级高于“prefix ++/--”,但这是唯一的理由吗?

The spec mentioned it in section "14.2.1 Operator precedence and associativity".规范在“14.2.1 运算符优先级和关联性”一节中提到了它。

So my very neutral question: Why are Postfix ++/-- categorized as primary Operators in C#?所以我非常中性的问题:为什么后缀 ++/-- 在 C# 中被归类为主要运算符? Is there a deeper truth in it?这里面有更深的真相吗?

EDIT: Okay, now I'm back home, I've removed most of the confusing parts...编辑:好的,现在我回家了,我已经删除了大部分令人困惑的部分......

I don't know why x++ is classified as a primary expression but ++x isn't;我不知道为什么x++被归类为主要表达式,但++x不是; although I doubt it makes much difference in terms of the code you would write.尽管我怀疑它在您编写的代码方面有很大的不同。 Ditto precedence.同上优先。 I wonder whether the postfix is deemed primary as it's used more commonly?我想知道后缀是否被认为是主要的,因为它更常用? The annotated C# specs don't have any annotations around this, by the way, in either the ECMA edition or the Microsoft C# 4 editions.顺便说一句,在 ECMA 版本或 Microsoft C# 4 版本中,带注释的 C# 规范没有任何注释。 (I can't immediately find my C# 3 edition to check.) (我无法立即找到我的 C# 3 版本进行检查。)

However, in terms of implementation, I would think of ++ as a sort of pseudo-operator which is used by both prefix and postfix expressions.但是,在实现方面,我认为++是一种伪运算符,前缀和后缀表达式都使用它。 In particular, when you overload the ++ operator, that overload is used for both postfix and prefix increment.特别是,当您重载++运算符时,该重载用于后缀和前缀增量。 This is unlike C++, as stakx pointed out in a comment.正如 stakx 在评论中指出的那样,这与 C++ 不同。

One thing to note is that while a post-increment/post-decrement expression has to have a primary expression as an operand, a pre-increment/pre-decrement expression only has to have a unary expression as an operand.需要注意的一点是,虽然后自增/后自减表达式必须有一个主表达式作为操作数,但前自增/预自减表达式只需要将一元表达式作为操作数。 In both cases the operand has to be classified as a variable, property access or indexer access though, so I'm not sure what practical difference that makes, if any.在这两种情况下,操作数都必须归类为变量、属性访问或索引器访问,所以我不确定这会产生什么实际差异(如果有的话)。

EDIT: Just to give another bit of commentary, even though it seems arbitrary, I agree it does seem odd when the spec states:编辑:只是再发表一点评论,即使它看起来是任意的,我同意当规范声明时它看起来确实很奇怪:

Primary expressions include the simplest forms of expressions主要表达式包括最简单的 forms 表达式

But the list of steps for pre-increment is shorter/simpler than list of steps for post-increment (as it doesn't include the "save the value" step).但是预增量的步骤列表比后增量的步骤列表更短/更简单(因为它不包括“保存值”步骤)。

Since the ECMA standard itself does not define what a 'Primary' operator is, other than order of precedence (ie coming before 'Unary') there can be no other significance.由于ECMA 标准本身并没有定义什么是'Primary' 运算符,所以除了优先顺序(即在'Unary' 之前)之外,没有其他意义。 The choice of words was probably bad.措辞的选择可能很糟糕。

Take into account that in many C-link languages, postfix operators tend to create a temporary variable where the expression's intermediate result is stored (see: "Prefer prefix operators over postfix" at Semicolon ).考虑到在许多 C-link 语言中,后缀运算符倾向于创建一个临时变量来存储表达式的中间结果(请参阅: 分号中的“优先使用前缀运算符而不是后缀” )。 Thus, they are fundamentally different from the prefix version.因此,它们与前缀版本根本不同。

Nonetheless, quickly checking how Mono and Visual Studio compile for-loops using the postfix and prefix forms, I saw that the IL code produced is identical.尽管如此,快速检查 Mono 和 Visual Studio 如何使用后缀和前缀 forms 编译 for 循环,我发现生成的 IL 代码是相同的。 Only if you use the postfix/prefix expression's value does it translate to different IL (only affecting where the 'dup' instruction in placed), at least with those implementations mentioned.仅当您使用后缀/前缀表达式的值时,它才会转换为不同的 IL(仅影响 'dup' 指令的放置位置),至少对于提到的那些实现。

the difference is that a[i++] will access the element indexed i.不同之处在于 a[i++] 将访问索引为 i 的元素。

a[++i] will access teh element indexed i+1. a[++i] 将访问索引为 i+1 的元素。

In both cases after execution of a[++i/i++]在执行 a[++i/i++] 之后的两种情况下

i will be i+1.我将是 i+1。

This can make troubles because you can't make assumption on parameters order这可能会造成麻烦,因为您无法对参数顺序做出假设

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

will increment i 3 times but you don't know in wich order.将 i 增加 3 次,但您不知道顺序。 if initially i is 4 you can also have function(4,5,6)如果最初 i 是 4,你也可以拥有 function(4,5,6)

but also function(6,5,4) or also function(6,4,5).还有函数(6,5,4)或函数(6,4,5)。

and that is still nothing because I used as example native types (for example "int"), things get worse when you have classes.这仍然算不了什么,因为我将本机类型用作示例(例如“int”),当你有类时情况会变得更糟。

When overloading the operator result is not changed, what is changed is it's precedence.当重载操作符结果没有改变时,改变的是它的优先级。 and this too can cause troubles.这也会引起麻烦。

So in one case "++" is applied before returning the reference, in the other case is applied "after" returning the reference.因此,在一种情况下,在返回引用之前应用“++”,在另一种情况下,在返回引用之后应用。 And when you overload it probably is better having it applied before returnin the reference (so ++something is much better than something++ at least from overloading point of view.)当你重载时,在返回引用之前应用它可能会更好(所以 ++something 比 something++ 好得多,至少从重载的角度来看。)

take a generic class with overloaded ++ (of wich we have 2 items, foo and bar)采用带有重载 ++ 的通用 class (我们有 2 个项目,foo 和 bar)

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

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

and there's much difference.并且有很大的不同。 Especially when you just don't assign your reference but do something more complex with it, or internally your object has stuff that has to do with shallow-copies VS deep copies.尤其是当您只是不分配您的参考,而是用它做一些更复杂的事情,或者在内部您的 object 有一些与浅拷贝和深拷贝有关的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM