Это разрешено:
int a, b, c;
a = b = c = 16;
string s = null;
while ((s = "Hello") != null) ;
Насколько я понимаю, присвоение s = ”Hello”;
должно вызывать “Hello”
только назначение s
, но операция не должна возвращать никакого значения. Если бы это было правдой, то ((s = "Hello") != null)
выдала бы ошибку, так null
как сравнивалась бы ни с чем.
В чем причина разрешения операторам присваивания возвращать значение?
while ((s = GetWord()) != null) Process(s);
это не так).Ответы:
Ваше понимание неверно на 100%. Можете ли вы объяснить, почему вы верите в эту ложь?
Во-первых, операторы присваивания не производят значения. Выражения присваивания производят значение. Выражение присваивания - это законный оператор; есть только несколько выражений, которые являются допустимыми операторами в C #: ожидающие выражения, построение экземпляра, приращение, декремент, выражения вызова и присваивания могут использоваться там, где ожидается оператор.
В C # есть только один вид выражения, который не производит какого-либо значения, а именно вызов чего-то, типизированного как возвращающий void. (Или, что то же самое, ожидание задачи без связанного значения результата.) Любой другой вид выражения производит значение или переменную, или ссылку, или доступ к свойству, или доступ к событию, и так далее.
Обратите внимание, что все выражения, которые допустимы в качестве операторов, полезны из-за их побочных эффектов . Это ключевая идея, и я думаю, что, возможно, причина вашей интуиции в том, что присваивания должны быть операторами, а не выражениями. В идеале у нас должен быть ровно один побочный эффект для каждого оператора и никаких побочных эффектов в выражении. Это является немного странным , что бок осуществления код могут быть использованы в контексте выражения вообще.
Причина, по которой разрешена эта функция, заключается в том, что (1) она часто удобна и (2) она идиоматична для C-подобных языков.
Можно заметить, что возник вопрос: почему эта идиоматика в C-подобных языках?
К сожалению, Деннис Ричи больше не доступен для запроса, но я предполагаю, что присвоение почти всегда оставляет после себя значение, которое было только что присвоено в регистре. C - это язык, очень близкий к машинному. Кажется правдоподобным и согласуется с дизайном C, что есть функция языка, которая в основном означает «продолжать использовать значение, которое я только что назначил». Для этой функции очень легко написать генератор кода; вы просто продолжаете использовать регистр, в котором хранится присвоенное значение.
источник
=
/==
, которую легко устранить, запретив использовать значение,=
если оно не заключено в скобки. например,if ((x = y))
илиif ((x = y) == true)
разрешено, ноif (x = y)
нет.Разве вы не ответили? Это необходимо для того, чтобы задействовать именно те конструкции, которые вы упомянули.
Обычный случай, когда используется это свойство оператора присваивания, - это чтение строк из файла ...
источник
return _myBackingField ?? (_myBackingField = CalculateBackingField());
гораздо меньше хлопот, чем проверка на null и присваивание.Я больше всего использую выражения присваивания для лениво инициализированных свойств.
источник
??=
синтаксис.Во-первых, он позволяет вам связывать свои задания, как в вашем примере:
Во-вторых, это позволяет вам назначать и проверять результат в одном выражении:
Обе причины, возможно, сомнительны, но определенно есть люди, которым нравятся эти конструкции.
источник
return (HttpContext.Current.Items["x"] = myvar);
Помимо уже упомянутых причин (цепочка назначений, установка и проверка в циклах while и т. Д.), Для правильного использования
using
оператора вам понадобится эта функция:MSDN не рекомендует объявлять одноразовый объект вне оператора using, поскольку он останется в области действия даже после его удаления (см. Статью MSDN, на которую я ссылался).
источник
using
. Все они являются законными:using (X x = new X())
,using (x = new X())
,using (x)
. Однако в этом примере содержимое оператора using представляет собой специальный синтаксис, который вообще не полагается на присваивание, возвращающее значение -Font font3 = new Font("Arial", 10.0f)
не является выражением и недопустимо в любом месте, которое ожидает выражения.Я хотел бы подробнее остановиться на конкретном моменте, который Эрик Липперт высказал в своем ответе, и обратить внимание на особый случай, который вообще никем не затронут. Эрик сказал:
Я хотел бы сказать, что присвоение всегда будет оставлять после себя значение, которое мы пытались присвоить нашему левому операнду. Не просто «почти всегда». Но я не знаю, потому что я не нашел комментария к этой проблеме в документации. Теоретически может быть очень эффективной реализованной процедурой «оставить позади» и не переоценивать левый операнд, но эффективна ли она?
«Эффективно» да для всех примеров, построенных на данный момент в ответах этой ветки. Но эффективно в случае свойств и индексаторов, использующих методы доступа get и set? Не за что. Рассмотрим этот код:
Здесь у нас есть свойство, которое даже не является оболочкой для частной переменной. Всякий раз, когда к нему обращаются, он должен возвращать истину, всякий раз, когда кто-то пытается установить свое значение, он ничего не должен делать. Таким образом, всякий раз, когда это свойство оценивается, он должен быть правдивым. Давай посмотрим что происходит:
Угадайте, что он печатает? Он печатает
Unexpected!!
. Как оказалось, действительно вызывается метод доступа set, который ничего не делает. Но после этого метод доступа get вообще никогда не вызывается. Присваивание просто оставляетfalse
значение, которое мы пытались присвоить нашему свойству. И этоfalse
значение оценивается оператором if.Я закончу реальным примером, который побудил меня исследовать эту проблему. Я сделал индексатор, который был удобной оболочкой для collection (
List<string>
), который мой класс имел в качестве частной переменной.Параметр, отправленный индексатору, представлял собой строку, которую следовало рассматривать как значение в моей коллекции. Метод доступа get просто вернет true или false, если это значение существует в списке или нет. Таким образом, метод доступа get был еще одним способом использования
List<T>.Contains
метода.Если метод доступа set индексатора был вызван со строкой в качестве аргумента, а правый операнд был логическим значением
true
, он бы добавил этот параметр в список. Но если тот же параметр был отправлен в метод доступа, а правый операнд был логическим значениемfalse
, он вместо этого удалял бы элемент из списка. Таким образом, набор аксессуаров использовался как удобная альтернатива обоимList<T>.Add
иList<T>.Remove
.Я думал, что у меня есть аккуратный и компактный «API», который оборачивает список моей собственной логикой, реализованной в качестве шлюза. С помощью одного только индексатора я мог многое делать с помощью нескольких нажатий клавиш. Например, как я могу попытаться добавить значение в свой список и убедиться, что оно там есть? Я думал, что это единственная необходимая строка кода:
Но, как показал мой предыдущий пример, метод доступа get, который должен видеть, действительно ли значение находится в списке, даже не был вызван.
true
Значение всегда было оставлено позади эффективно уничтожая любую логику я осуществил в моем ПОЛУЧИТЬ аксессор.источник
Если присваивание не вернуло значение, строка
a = b = c = 16
тоже не будет работать.Также
while ((s = readLine()) != null)
иногда может быть полезно умение писать такие вещи .Итак, причина, по которой присваивание возвращает присвоенное значение, заключается в том, чтобы позволить вам делать эти вещи.
источник
=
появлении в выражении, которое не заключено в скобки (не считая скобок в самом выражении if / while). gcc выдает такие предупреждения и тем самым по существу устранил этот класс ошибок из программ C / C ++, которые скомпилированы с ним. Жаль, что другие разработчики компиляторов уделили так мало внимания этой и нескольким другим хорошим идеям в gcc.Я думаю, вы не понимаете, как парсер будет интерпретировать этот синтаксис. Сначала будет оценено присвоение , а затем результат будет сравнен с NULL, т. Е. Оператор эквивалентен:
Как указывали другие, результатом присвоения является присвоенное значение. Мне трудно представить себе преимущества наличия
((s = "Hello") != null)
и
не быть эквивалентным ...
источник
Я думаю, что основная причина - это (преднамеренное) сходство с C ++ и C. Приведение оператора присваивания (и множества других языковых конструкций) к поведению их аналогов на C ++ просто следует принципу наименьшего удивления, и любой программист, пришедший из другого фигурного- язык скобок может использовать их, не особо задумываясь. Быть понятным программистам на C ++ было одной из основных целей разработки C #.
источник
По двум причинам, которые вы указываете в своем сообщении:
1) чтобы вы могли сделать
a = b = c = 16
2) чтобы вы могли проверить, выполнено ли задание
if ((s = openSomeHandle()) != null)
источник
Тот факт, что 'a ++' или 'printf ("foo")' могут быть полезны либо как автономный оператор, либо как часть большего выражения, означает, что C должен учитывать возможность того, что результаты выражения могут быть или не быть используемый. Учитывая это, существует общее представление о том, что выражения, которые могут с пользой «возвращать» значение, также могут это делать. Цепочка присвоений может быть немного «интересной» в C и даже более интересной в C ++, если все рассматриваемые переменные не имеют в точности один и тот же тип. Такого использования, вероятно, лучше всего избегать.
источник
Еще один отличный пример использования, я использую его все время:
источник
Дополнительное преимущество, которое я не вижу здесь в ответах, заключается в том, что синтаксис для назначения основан на арифметике.
Теперь
x = y = b = c = 2 + 3
в арифметике означает нечто иное, чем язык C-стиля; в арифметике это утверждение, мы утверждаем, что x равно y и т. д., а в языке C-стиля это инструкция, которая делает x равным y и т. д. после ее выполнения.Тем не менее, между арифметикой и кодом все еще достаточно взаимосвязи, поэтому нет смысла запрещать то, что является естественным в арифметике, если нет веской причины. (Другая вещь, которую языки C-стиля взяли из использования символа равенства, - это использование == для сравнения на равенство. Хотя здесь, поскольку крайний правый == возвращает значение, такое связывание было бы невозможно.)
источник
a = b = c
означает, чтоa
иb
иc
являются одним и тем же. Изучая язык в стиле C, мы изучаем это послеa = b = c
,a
иb
это то же самое, что иc
. Конечно, есть разница в семантике, как говорит сам мой ответ, но все же, когда в детстве я впервые научился программировать на языке, который действительно использовался=
для назначения, но не позволял,a = b = c
это казалось мне неразумным, хотя…=
для сравнения на равенство, поэтому в немa = b = c
должно быть означать, чтоa = b == c
означает в языках C-стиля. Я обнаружил, что создание цепочек, разрешенных в C, намного более интуитивно понятно, потому что я мог провести аналогию с арифметикой.Мне нравится использовать возвращаемое значение присваивания, когда мне нужно обновить кучу вещей и вернуть, были ли изменения или нет:
Но будьте осторожны. Вы можете подумать, что можете сократить его до этого:
Но на самом деле это перестанет оценивать операторы or после того, как обнаружит первое истинное. В данном случае это означает, что он перестает присваивать последующие значения после того, как присваивает первое другое значение.
См. Https://dotnetfiddle.net/e05Rh8, чтобы поиграть с этим
источник