Цепочки вызовов простых операторов присваивания не такие уж и простые
Дополнение: Я прерываю этот эпизод « Невероятных приключений » в коде просьбой моего друга и коллеги Лукиана ( Lucian ), из команды VB , который интересуется, является ли распространенным использование в C # того факта, что присваивание является выражением ( expression ). Темой сообщения является наиболее распространенное использование данного шаблона: тот факт, что «цепочки вызовов» операторов присваивания вообще работают, является следствием того, что присваивания являются выражениями ( expressions ), а не операторами ( statements ). Существуют также и разные сценарии использования: кто-то может представить что-то вроде " returnthis . myField = x ;" в качестве более короткой записи для " this . myField = x ; returnthis . myField ;" для случая, когда мы выполняем некоторые вычисления и сохраняем результаты для последующего использования. Или, например, у нас есть что-то вроде myNonNullableString = ( myNullableString = Foo ()) ?? "< null >";. Существует несколько вариантов, когда эта идиома может применяться.
Сам я никогда не пользуюсь этой идиомой. Я считаю, что побочные эффекты присваивания, лучше всего видны при использовании отдельных операторов для каждого выражения, а не тогда, когда они встроены в более крупные конструкции. У меня к вам следующий вопрос: используете ли вы присваивание как выражение? Если да, как и почему? Обратите внимание, что мне нужны простые примеры использования этого шаблона в «реальной жизни», а не хитрые идеи о том, как он может быть теоретически использован. Если у вас есть такие примеры, пожалуйста, напишите их в комментариях, и я передам их Лукиану. Спасибо!
*********************
Сегодня я проверю еще один миф о языке C#. Давайте рассмотрим следующий код:
a = b = c;
Код корректен. Вы можете использовать цепочки вызовов операторов присваивания произвольной длины. Этот шаблон чаще всего применяется в конструкциях вида:
int i, j, k;
i = j = k = 123;
Я часто слышу, что код работает «потому что оператор присваивания правоассоциативен, а результатом присваивания является значение правой части выражения».
На самом деле это высказывание верно только наполовину. Выражение правоассоциативно. Очевидно, что предыдущий пример эквивалентен следующему коду:
i = (j = (k = 123)));
Нет никакого смысла заключать выражение в скобки с левой стороны. Итак, в этом конкретном примере, высказывание верно, но в общем случае – нет. Результатом выполнения оператора присваивания не является значение правой части выражения.
const int x = 10;
short y;
object z;
z = y = x;
System.Console.WriteLine(z.GetType().ToString());
Этот фрагмент выводит “System.Int16”, а не “System.Int32”. Значение правой части “y = x” явно имеет тип int, но переменной z мы присваиваем не ссылку на упакованный int, а ссылку на упакованный short!
В таком случае, является ли корректным высказывание «…результатом присваивания является значение левой части выражения»?
Нет, это высказывание неверно и мы можем доказать это.
class C
{
private string x;
public string X {
get { return x ?? ""; }
set { x = value; } }
static void Main()
{
C c = new C();
object z;
z = c.X = null;
System.Console.WriteLine(z == null);
System.Console.WriteLine(c.X == null);
}
}
Этот фрагмент выводит “True / False”. Результатом оператора присваивания не является значение левой части выражения. Значение левой части – это пустая строка, но значение оператора равно null.
Черт возьми, левая часть выражения может даже не содержать значения. Свойства доступные только для записи загадочные и достаточно редкие, но вполне корректные. Если у нас бы не было метода получения значения свойства (getter), в таком случае левая часть выражения c.X не содержала бы значения!
Теперь достаточно просто сформулировать правильное высказывание: результатом простого оператора присваивания является значение, присвоенное левой части.