Почему операторы присваивания возвращают значение?
==================================================

Это разрешено:

Насколько я понимаю, назначение`s = ”Hello”;` должен только вызвать`“Hello”` быть назначенным на`s`, но операция не должна возвращать никакого значения. Если это было правдой, то`((s = "Hello") != null)` выдаст ошибку, так как`null` будет по сравнению с ничем.

Какова причина, позволяющая операторам присваивания возвращать значение?

Насколько я понимаю, присваивание s = "Привет"; должен вызывать только s «Hello», но операция не должна возвращать никакого значения.

Ваше понимание на 100% неверно.**Можете ли вы объяснить, почему вы верите в эту ложную вещь?**

Какова причина, позволяющая операторам присваивания возвращать значение?

Во-первых, назначениезаявления не производите ценность. присваиваниевыражения произвести значение. Выражение присваивания является юридическим утверждением; в C # есть только несколько выражений, которые являются допустимыми утверждениями: ожидающие выражения, выражения конструкции, приращения, декремента, вызова и присваивания могут использоваться там, где ожидается выражение.

В C # есть только один вид выражения, который не производит какого-либо значения, а именно вызов чего-то, что типизируется как возвращающее void. (Или, что эквивалентно, ожидание задачи без ассоциированного значения результата.) Любой другой вид выражения создает значение, либо переменную, либо ссылку, либо доступ к свойству, либо доступ к событию и т. Д.

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

Причина, по которой это возможно, заключается в том, что (1) она часто удобна и (2) она идиоматична в C-подобных языках.

Можно заметить, что вопрос был задан: почему это идиоматично в C-подобных языках?

К сожалению, Деннис Ритчи больше не может спрашивать, но я думаю, что задание почти всегда остается позадизначение, которое было только что присвоено в реестре. С - это язык "очень близко к машине". Кажется правдоподобным, и в соответствии с замыслом C существует языковая функция, которая в основном означает «продолжай использовать значение, которое я только что присвоил». Для этой функции очень легко написать генератор кода; вы просто продолжаете использовать регистр, в котором хранится присвоенное значение.



Вы не предоставили ответ? Это для того, чтобы включить именно те конструкции, которые вы упомянули.

Распространенным случаем, когда используется это свойство оператора присваивания, является чтение строк из файла ...



Мое любимое использование выражений присваивания для лениво инициализированных свойств.



Я думаю, что основной причиной является (преднамеренное) сходство с C ++ и C. Заставить оператор присвоения (и многие другие языковые конструкции) вести себя так же, как их аналоги в C ++, просто следуя принципу наименьшего удивления, и любой программист приходит из другого вьющегося язык скобок может использовать их, не тратя много времени. Быть легко подобранным для программистов на C ++ было одной из главных целей разработки для C #.



Дополнительное преимущество, которое я не вижу в ответах здесь, состоит в том, что синтаксис для назначения основан на арифметике.

Сейчас`x = y = b = c = 2 + 3` означает нечто иное в арифметике, чем язык в стиле C; в арифметике это утверждение, мыгосударство что х равен у и т. д. и на языке стиля С это инструкция, котораямарки х равно у и т. д. после его выполнения.

Тем не менее, между арифметикой и кодом все еще достаточно взаимосвязи, поэтому нет смысла запрещать то, что является естественным в арифметике, если нет веской причины. (Другая вещь, которую языки C-стиля взяли от использования символа равенства, - это использование == для сравнения на равенство. Здесь, хотя, поскольку крайний правый == возвращает значение, такого рода цепочки были бы невозможны.)



Я думаю, вы неправильно понимаете, как синтаксический анализатор будет интерпретировать этот синтаксис. Задание будет оцененопервый, а затем результат будет сравниваться с NULL, то есть оператор эквивалентен:

Как уже отмечали другие, результатом присваивания является присвоенное значение. Мне трудно представить преимущество наличия

`((s = "Hello") != null)`

а также

не быть эквивалентным ...



Помимо уже упомянутых причин (цепочка назначений, настройка и проверка в циклах while и т. Д.),[должным образом](https://msdn.microsoft.com/en-us/library/yh598w02.aspx) использовать`using` Скажите, вам нужна эта функция:

MSDN не рекомендует объявлять одноразовый объект вне оператора using, так как он останется в области действия даже после его удаления (см. Статью MSDN, на которую я ссылаюсь).



По двум причинам, которые вы включаете в свой пост  
1) так что вы можете сделать`a = b = c = 16`  
2) чтобы вы могли проверить, успешно ли выполнено задание`if ((s = openSomeHandle()) != null)`



С одной стороны, это позволяет вам связывать свои назначения, как в вашем примере:

Для другого, это позволяет вам присваивать и проверять результат в одном выражении:

Обе, возможно, сомнительные причины, но определенно есть люди, которым нравятся эти конструкции.



Если присвоение не вернуло значение, строка`a = b = c = 16` тоже не сработает.

Также возможность писать такие вещи, как`while ((s = readLine()) != null)` может быть полезным иногда.

Таким образом, причина, по которой назначение позволяет вернуть назначенное значение, состоит в том, чтобы позволить вам делать эти вещи.



Еще один отличный пример использования, я использую это все время:



Я хотел бы остановиться на конкретном моменте, который Эрик Липперт сделал в своем ответе, и уделить особое внимание конкретному случаю, который никто еще не затронул. Эрик сказал:

[...] присваивание почти всегда оставляет значение, только что назначенное в регистре.

Я хотел бы сказать, что присвоение всегда будет оставлять позади значение, которое мы пытались присвоить нашему левому операнду. Не просто "почти всегда". Но я не знаю, потому что я не нашел эту проблему в документации. Теоретически это может быть очень эффективная реализованная процедура, чтобы «оставить позади», а не переоценивать левый операнд, но эффективна ли она?

«Эффективно» да для всех примеров, построенных до сих пор в ответах этой ветки. Но эффективны ли в случае свойств и индексаторов, которые используют методы доступа get и set? Не за что. Рассмотрим этот код:

Здесь у нас есть свойство, которое даже не является оберткой для приватной переменной. Всякий раз, когда к нему обращаются, он должен возвращать истину, всякий раз, когда кто-то пытается установить свою ценность, он ничего не делает. Таким образом, всякий раз, когда это свойство оценивается, он должен быть правдивым. Давай посмотрим что происходит:

Угадайте, что это печатает? Это печатает`Unexpected!!`, Как выясняется, метод доступа set действительно вызывается, что ничего не делает. Но после этого метод доступа get никогда не вызывается вообще. Назначение просто оставляет позади`false` Значение, которое мы пытались присвоить нашей собственности. И это`false` значение - это то, что вычисляет оператор if.

**Я закончу с примером из реального мира** это заставило меня исследовать эту проблему. Я сделал индексатор, который был удобной оберткой для коллекции (`List<string>`) что мой класс имел в качестве частной переменной.

Параметр, отправленный индексатору, был строкой, которую нужно было рассматривать как значение в моей коллекции. Метод доступа get просто возвращает true или false, если это значение существует в списке или нет. Таким образом, метод доступа get был еще одним способом использования`List<T>.Contains` метод.

Если метод доступа индексатора был вызван со строкой в качестве аргумента и правильный операнд был bool`true`он добавил бы этот параметр в список. Но если тот же параметр был отправлен в метод доступа, а правым операндом был бул`false`вместо этого он удалил бы элемент из списка. Таким образом, доступный набор использовался как удобная альтернатива обоим`List<T>.Add` а также`List<T>.Remove`.

Я думал, что у меня был аккуратный и компактный «API», оборачивающий список своей собственной логикой, реализованной как шлюз. С помощью только одного индексатора я мог сделать много вещей с помощью нескольких нажатий клавиш. Например, как я могу попытаться добавить значение в мой список и убедиться, что оно там? Я думал, что это была единственная необходимая строка кода:

Но, как показал мой предыдущий пример, метод доступа get, который должен видеть, действительно ли значение находится в списке, даже не вызывался.`true` Значение всегда оставалось позади, эффективно разрушая любую логику, которую я реализовал в своем методе доступа get.



Тот факт, что «a ++» или «printf (« foo »)» может быть полезен либо как самостоятельное утверждение, либо как часть более крупного выражения, означает, что C должен учитывать возможность того, что результаты выражения могут быть или не быть используемый. Учитывая это, существует общее понятие, что выражения, которые могут с пользой «возвращать» значение, могут также делать это. Цепочка назначений может быть немного «интересной» в C и даже более интересной в C ++, если все рассматриваемые переменные не имеют точно одинаковый тип. Такое использование, вероятно, лучше избегать.