Подтвердить что ты не робот

Оператор '=' цепочка в С# - этот тест должен пройти?

Я просто писал настройщик свойств и имел мозговую волну о том, почему нам не нужно return результат set, когда свойство может быть задействовано в цепочке оператора =, т.е.

var a = (b.c = d);

(Я добавил скобки для ясности, но на практике это не имеет никакого значения)

Я начал думать - где компилятор С# получает значение, которое присваивается a в приведенном выше примере?

Логика говорит, что это должно быть из результата операции (b.c = d), но поскольку это реализовано с помощью метода void set_blah(value), это не может быть.

Таким образом, доступны только другие опции:

  • Перечитайте b.c после назначения и используйте это значение
  • Повторно использовать d

  • Изменить (после ответа и комментариев от Эрика) - есть третий вариант, который является тем, что делает С#: используйте значение, записанное в b.c после того, как произошли какие-либо преобразования

Теперь, на мой взгляд, правильное чтение вышеуказанной строки кода

установите a в результат установки b.c в d

Я думаю, что разумное чтение кода - поэтому я решил проверить, действительно ли это происходит со слегка надуманным тестом, - но спросите себя, думаете ли вы, что он должен пройти или провалиться:

public class TestClass
{
  private bool _invertedBoolean;
  public bool InvertedBoolean
  {
    get
    {
      return _invertedBoolean;
    }
    set
    {
      //don't ask me why you would with a boolean,
      //but consider rounding on currency values, or
      //properties which clone their input value instead
      //of taking the reference.
      _invertedBoolean = !value;
    }
  }
}

[TestMethod]
public void ExampleTest()
{
  var t = new TestClass();
  bool result;
  result = (t.InvertedBoolean = true);
  Assert.IsFalse(result);
}

Этот тест не выполняется.

Более подробное рассмотрение ИЛ, созданного для кода, показывает, что значение true загружается в стек, клонированное командой dup, а затем оба выпадаются в двух последовательных присваиваниях.

Этот метод отлично работает для полей, но для меня кажется ужасно наивным для свойств, где каждый на самом деле является вызовом метода, где фактическое значение конечного свойства не гарантируется как входное значение.

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

Возможно, я действительно толстый, но для меня это предполагает неправильную реализацию этого шаблона компилятором (.Net 4 btw). Но тогда мое ожидание/чтение кода неверно?

4b9b3361

Ответ 1

Результат присваивания x = {expr} определяется как значение, полученное из {expr}.

§14.14.1 Простое присвоение (ECMA334 v4)

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

Обратите внимание, что назначенное значение - это уже оцененное значение из d. Следовательно, реализация здесь:

var tmp = (TypeOfC)d;
b.c = tmp;
a = tmp;

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

Ответ 2

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

Как вы поняли, мы делаем все, что в наших силах, чтобы избежать этого состояния. Это хорошая вещь. Когда вы говорите "x = y = z" , то, если это вообще возможно, мы должны гарантировать, что x и y в конечном итоге назначают одно и то же значение - значение z - даже если y - какая-то сумасшедшая вещь, которая не удерживает значение, которое вы дай это. "x = y = z" должно логически быть "y = z, x = z", за исключением того, что z оценивается только один раз. y не входит в вопрос вообще при назначении x; почему это должно быть?

Кроме того, конечно, когда вы выполняете "x = y = z" , мы не можем последовательно "повторно использовать" y, потому что y может быть единственным свойством записи. Что делать, если нет считывателя для чтения значения из?

Кроме того, я отмечаю, что вы говорите "это работает для полей" - не если поле является изменчивым, это не так. У вас нет никакой гарантии, что назначенное вами значение является значением, которое поле берет, если оно является изменчивым полем. У вас нет гарантии, что значение, которое вы читали из изменчивого поля в прошлом, - это значение поля.

Подробнее о этой теме см. в моей статье:

http://blogs.msdn.com/b/ericlippert/archive/2010/02/11/chaining-simple-assignments-is-not-so-simple.aspx

Ответ 3

Оператор присваивания документирован, чтобы вернуть результат вычисления его второго операнда (в этом случае b). Не имеет значения, что он также присваивает это значение своему первому операнду, и это назначение выполняется путем вызова метода, который возвращает void.

Спектр говорит:

14.14.1 Простое назначение

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

Итак, на самом деле происходит следующее:

  • d оценивается (пусть вызывается полученное значение val)
  • результат присваивается b.c
  • Результат оператора присваивания val
  • a присваивается значение val
  • результат второго оператора присваивания также равен val (но так как все выражение заканчивается здесь, оно не используется)