Я думаю, что поведение компилятора довольно логично и ожидаемо.
В следующем коде:
int i;
int j;
var k = i + j;
Существует точная перегрузка для этой операции, так k
является int
. Такая же логика применяется при добавлении двух uint
, двух byte
или что у вас есть. Работа компилятора здесь проста, она счастлива, потому что разрешение перегрузки находит точное совпадение. Существует довольно хороший шанс, что человек, пишущий этот код, ожидает, что k
будет int
и знает, что операция может переполняться при определенных обстоятельствах.
Теперь рассмотрим случай, вы спрашиваете о:
uint i;
int j;
var k = i + j;
Что видит компилятор? Ну, он видит операцию, которая не имеет соответствующей перегрузки; нет оператора +
перегрузки, который принимает int
и uint
в качестве двух операндов. Таким образом, алгоритм разрешения перегрузки продолжается и пытается найти перегрузку оператора, которая может быть действительной. Это означает, что он должен найти перегрузку, где задействованные типы могут «удерживать» исходные операнды; то есть как i
, так и j
должны быть неявно конвертируемыми в указанный тип (ы).
Компилятор не может неявно конвертировать uint
в int
, потому что такое преобразование не exsit. Он неявно конвертирует int
в uint
либо из-за того, что это преобразование также не существует (оба могут вызвать изменение величины). Таким образом, единственным выбором, который он действительно имеет, является выбор первого более широкого типа, который может «удерживать» оба типа операндов, который в этом случае равен long
. Как только оба операнда будут неявно преобразован в long
k
, являющийся long
, является очевидным.
Мотивация этого поведения - ИМО, чтобы выбрать самый безопасный доступный вариант, а не второй, угадать намерение сомнительного кодера. Компилятор не может сделать обоснованное предположение о том, что ожидается, что человек, пишущий этот код, будет k
. int
? Ну, почему бы не uint
? Оба варианта кажутся одинаково плохими. Компилятор выбирает единственный логический путь; безопасный: long
. Если кодер хочет, чтобы k
был либо int
, либо unit
, ему нужно явно указать один из операндов.
И последнее, но не менее важное, алгоритм перегрузки по алгоритму компилятора C# не учитывает тип возврата при выборе наилучшей перегрузки. Таким образом, факт, что вы сохраняете результат операции в uint
, совершенно не имеет отношения к компилятору и не имеет никакого эффекта в процессе разрешения перегрузки.
Это все спекуляции с моей стороны, и я могу быть совершенно неправым. Но это делает кажется логичным рассуждением.
Ознакомьтесь с ответом Эрика Липперта ниже. Он был фактически вовлечен в дизайн C#, поэтому его единственный ответ здесь - на «мотивацию для этого решения», который является авторитетным. –
Да, это действительно здорово, чтобы получить ответ от Эрика Липперта. –