Myślę, że zachowanie kompilatora jest dość logiczne i oczekiwane.
W poniższym kodzie:
int i;
int j;
var k = i + j;
Jest dokładny przeciążenie dla tej operacji, więc k
jest int
. Ta sama logika ma zastosowanie przy dodawaniu dwóch uint
, dwóch byte
lub co masz. Praca kompilatora jest łatwa, jest szczęśliwa, ponieważ rozdzielczość przeciążenia znajduje dokładne dopasowanie. Istnieje spora szansa, że osoba pisząca ten kod oczekuje, że k
będzie numerem int
i jest świadoma, że operacja może zostać przepełniona w pewnych okolicznościach.
Rozważmy teraz przypadek Pytasz o:
uint i;
int j;
var k = i + j;
Co robi kompilator zobaczyć? Cóż, widzi operację, która nie ma odpowiedniego przeciążenia; nie ma przeciążenia operatora, które pobiera i uint
jako jego dwa argumenty. Dlatego algorytm rozwiązywania przeciążenia idzie naprzód i próbuje znaleźć przeciążenie operatora, które może być ważne. Oznacza to, że musi znaleźć przeciążenie, w którym typy mogą "trzymać" oryginalne operandy; to znaczy, że zarówno i
, jak i j
muszą być niejawnie przekształcalne w wymieniony typ (typy).
Kompilator nie może domyślnie przekonwertować uint
na int
, ponieważ taka konwersja się nie powiodła. Nie można niejawnie przekonwertować int
na uint
, ponieważ ta konwersja również nie istnieje (obie mogą spowodować zmianę wielkości). Tak więc jedynym wyborem, jaki naprawdę ma, jest wybór pierwszego szerszego typu, który może "pomieścić" oba typy argumentów, co w tym przypadku to long
. Kiedy oba argumenty są niejawnie przekształcony w long
k
będący long
jest oczywisty.
Motywacją tego zachowania jest wybór przez IMO najbezpieczniejszej dostępnej opcji, a nie sekundowe odgadywanie intencji wątpliwego programisty. Kompilator nie może się domyślić, co osoba pisząca ten kod spodziewa się uzyskać. An int
? Dlaczego nie miałby to być uint
? Obie opcje wydają się równie złe. Kompilator wybiera jedyną ścieżkę logiczną; bezpieczny: long
. Jeśli programista chce, aby k
był albo int
lub unit
, musi tylko jawnie rzutować jeden z operandów.
I wreszcie, algorytm przeciążenia kompilatora C# nie bierze pod uwagę typu powrotu przy podejmowaniu decyzji o najlepszym przeciążeniu. Zatem fakt, że przechowujesz operację, powoduje, że uint
jest całkowicie nieistotny dla kompilatora i nie ma żadnego wpływu na proces rozwiązywania przeciążenia.
To wszystko spekulacje z mojej strony, i mogę się całkowicie mylić. Ale robi wydaje się logiczne rozumowanie.
Sprawdź poniżej odpowiedź Eric Lipperta. Był w rzeczywistości zaangażowany w projektowanie C#, więc jego jest jedyną odpowiedzią na "autorytatywność tej decyzji", która jest autorytatywna. –
Tak, to naprawdę fajnie mieć odpowiedź od Erica Lipperta. –