Re: [C#, Этюд] Compound assignment
От: Пельмешко Россия blog
Дата: 30.03.11 06:00
Оценка: 221 (11)
N>Какие из следующих программ скомпилируются и почему?

Здравствуйте, Владимир!

Чтобы ответить на Ваш вопрос, надо разобраться как работает compound assigment
Сначала операция рассматривается как обычный бинарный оператор и производится разрешение перегрузки оператора:

An operation of the form x op= y is processed by applying binary operator
overload resolution (§7.3.4) as if the operation was written x op y. Then,


Затем, если тип возвращаемого значения выбранного в результате overload resolution оператора имеет неявное приведение к типу левого операнда, то вычисляем операцию как x = x op y, за исключением того, что х вычисляется лишь один раз:

• If the return type of the selected operator is implicitly convertible to the type of x,
the operation is evaluated as x = x op y, except that x is evaluated only once.


В противном случае действует самое интересное правило:
то операция вычисляется как x = (T) (x op y), где T обозначает тип x, причём x вычисляется лишь один раз:

• Otherwise, if the selected operator is a predefined operator, if the return type of the
selected operator is explicitly convertible to the type of x, and if y is implicitly convertible
to the type of x or the operator is a shift operator, then the operation is evaluated as
x = (T)(x op y), where T is the type of x, except that x is evaluated only once.


Иначе падаем при компиляции:

• Otherwise, the compound assignment is invalid, and a binding-time error occurs.



Теперь рассмотрим все случаи:



byte x = 10;
x -= 1;

Тут уже не всё так просто, как кажется на первый взгляд. В ходе overload resolution будет выбран наиболее подходящий оператор:
int operator –(int x, int y);

Так как в C# нет предопределённого оператора для сложений чисел типа byte.
Неявного приведения типов int -> byte не существует, поэтому смотрим на второе правило:
Значит выражение будет успешно компилироваться.



byte x = 10;
x -= 1L;

Будет выбран следующий оператор:
long operator –(long x, long y);

Так как неявного приведения типов long -> byte не существует, то опять обращаемся ко второму правилу:
Однако не существует неявного приведения константного выражения 1L типа long к типу byte (так как константные выражения типа long могут неявно приводиться только к типу ulong, см. 6.1.9), поэтому данное выражение компилироваться не будет.



byte x = 10;
x += -1;

Overload resolution разрешится следующим оператором:
int operator +(int x, int y);

Снова второе правило:
Однако как и в предыдущем случае, не существует неявного приведения константного выражения -1 типа int к типу byte, так как значение -1 не попадает в диапазон чисел, представляемых беззнаковым типом byte. Данный код не будет компилироваться.



byte x = 10;
x += null;

В данном случае будет выбрана lifted-версия оператора сложения int'ов:
int? operator +(int? x, int? y);

Второе правило:
Опять же, не существует неявного приведения константного выражения null к типу bool, поэтому данный код не будет компилироваться.



byte x = 10;
x <<= null;

Тут будет найдена lifted-версия оператора сдвига:
int? operator <<(int? x, int? count);

Второе правило:
Значит выражение будет успешно компилироваться и во время выполнения будет падать с InvalidOperationException, так как выражение x << null будет вычислено в значение (int?) null, явное приведение которого к типу byte будет результировать исключением.

p.s. Я так понимаю, что аномалии с оператором сдвига обусловлены тем, что у него правый операнд всегда типа int.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.