Почему Postfix ++/- классифицируются как первичные операторы в С#?

В настоящее время я преподаю классу программистов на С++ основы языка С#. Поскольку мы обсуждали теговые операторы, я использовал стандартные категории С# первичных, унарных и т.д. Операторов.

Один из участников почувствовал себя озадаченным, потому что в стандарте С# "postfix ++/-" были помещены в категорию первичных операторов, а не в "префикc++/-" . Ее обоснование этой путаницы состояло в том, что она предпочла бы реализовать оператор С++ "postfix ++/-" в терминах оператора "prefix ++/-". Другими словами, она предпочла бы считать оператор "префикc++/-" основным оператором. - Я понимаю ее точку зрения, но я не могу дать ей обоснования. ОК, операторы "postfix ++/-" имеют более высокий приоритет, чем "префикc++/-" , но разве это единственное обоснование этого?

Спецификация упомянула его в разделе "14.2.1. Приоритет операторов и ассоциативность".

Итак, мой очень нейтральный вопрос: почему Postfix ++/- классифицируются как первичные операторы в С#? Есть ли в нем более глубокая правда?

Ответы

Ответ 1

Поскольку стандарт ECMA сам по себе не определяет, что такое "первичный" оператор, кроме порядка приоритета (т.е. предшествующего "унарному" ), другого значения не может быть. Выбор слов был, вероятно, плохим.

Учесть, что во многих языках C-link операторы постфикса имеют тенденцию создавать временную переменную, в которой сохраняется промежуточный результат выражения (см. "Предпочитаемый префикс операторы над постфиксами" в Семиколоне). Таким образом, они принципиально отличаются от префиксной версии.

Тем не менее, быстро проверяя, как Mono и Visual Studio компилируют for-loops с использованием форм постфикса и префикса, я увидел, что полученный код IL идентичен. Только если вы используете значение выражения postfix/prefix, оно переводится в другой IL (влияет только на то, где находится команда "dup" ), по крайней мере, с упомянутыми упомянутыми реализациями.

Ответ 2

EDIT: Хорошо, теперь я вернулся домой, я удалил большинство запутанных частей...

Я не знаю, почему x++ классифицируется как первичное выражение, но ++x не является; хотя я сомневаюсь, что это имеет большое значение с точки зрения кода, который вы напишете. Тоже приоритет. Интересно, считается ли постфикс первичным, поскольку он используется чаще? В аннотированных спецификациях С# нет комментариев об этом, кстати, либо в редакции ECMA, либо в изданиях Microsoft С# 4. (Я не могу сразу найти свою версию С# 3 для проверки.)

Однако, с точки зрения реализации, я бы подумал о ++ как о каком-то псевдооператоре, который используется как префиксом, так и постфиксными выражениями. В частности, когда вы перегружаете оператор ++, эта перегрузка используется для приращения постфикса и префикса. Это не похоже на С++, как указано в комментариях в stakx.

Следует отметить, что хотя выражение post-increment/post-декремент должно иметь первичное выражение в качестве операнда, выражение pre-increment/pre-decment должно иметь только унарное выражение в качестве операнда. В обоих случаях операнд должен быть классифицирован как переменная, доступ к свойствам или доступ к индексу, хотя я не уверен, какая практическая разница, если таковая имеется.

EDIT: просто чтобы дать еще немного комментариев, хотя это кажется произвольным, я согласен, что это кажется странным, когда спецификация заявляет:

Первичные выражения включают простейшие формы выражений

Но список шагов для предварительного инкремента короче/проще, чем список шагов для пост-инкремента (так как он не включает шаг "сохранить значение" ).

Ответ 3

разница в том, что a [i ++] получит доступ к элементу, индексированному i.

a [++ i] получит доступ к элементу с индексом я + 1.

В обоих случаях после выполнения [++ i/i ++]

i будет я + 1.

Это может вызвать проблемы, потому что вы не можете сделать предположение о порядке параметров

функция (я ++, я ++, я ++)

будет увеличиваться в 3 раза, но вы не знаете, в каком порядке. если изначально я равен 4, вы также можете Функция (4,5,6)

но также функция (6,5,4) или также функцию (6,4,5).

и это все равно ничего, потому что я использовал в качестве примера собственные типы (например, "int" ), все становится хуже, когда у вас есть классы.

При перегрузке результата оператора не изменяется, что изменилось - это приоритет. и это также может вызвать проблемы.

Итак, в одном случае "++" применяется перед возвратом ссылки, в другом случае применяется "после" возврата ссылки. И когда вы перегружаете его, лучше использовать его перед возвратом в ссылку (так что ++ что-то намного лучше, чем что-то ++, по крайней мере, с точки перегрузки).

возьмем общий класс с перегруженным ++ (из которого у нас есть 2 элемента, foo и bar)

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

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

и там большая разница. Особенно, когда вы просто не назначаете свою ссылку, но делаете с ней что-то более сложное или внутренне ваш объект имеет материал, который имеет отношение к мелким копиям VS глубоких копий.