Dlaczego inkrementacja wartości Nullable <int> nie zgłasza wyjątku?

98

Czy mógłbyś wyjaśnić, dlaczego Console.WriteLine zapisuje pusty wiersz ( Console.WriteLine(null)podaje mi błąd kompilacji) i dlaczego nie ma NullReferenceException (nawet a+=1nie powinien go zgłaszać)?

int? a = null;
a++; // Why there is not NullReferenceException? 
Console.WriteLine(a); // Empty line
Maxim Zhukov
źródło
Wszystkich trzech operatorów ++, +=i +zniosły warianty. Dlatego oświadczenia a++;, a += 1;i a = a + 1;wszystkie są dozwolone. Każdy produkt null(bez wyjątku), jeśli ajest początkowo null.
Jeppe Stig Nielsen
5
NullReferenceException? ale int?nie jest Reference, to po prostu intmoże mieć nullwartość
Khaled.K

Odpowiedzi:

124

Obserwujesz skutki uniesienia operatora .

Z sekcji 7.3.7 specyfikacji C # 5:

Operatory zniesione zezwalają na operatory predefiniowane i zdefiniowane przez użytkownika, które działają na typach wartości niedopuszczających wartości null, aby były również używane z formularzami tych typów dopuszczających wartość null. Podnoszone operatory są zbudowane z predefiniowanych i zdefiniowanych przez użytkownika operatorów, które spełniają określone wymagania, jak opisano poniżej:

  • W przypadku operatorów jednoargumentowych + ++ - -- ! ~ podniesiona forma operatora istnieje, jeśli operand i typy wyników są typami wartości niepodlegającymi wartości null. Podniesiona forma jest konstruowana przez dodanie pojedynczego ?modyfikatora do operandów i typów wyników. Podniesiony operator generuje wartość null, jeśli operand ma wartość null. W przeciwnym razie podniesiony operator rozpakowuje operand, stosuje operator bazowy i zawija wynik.

Zasadniczo więc a++w tym przypadku jest to wyrażenie z wynikiem null(jako int?), a zmienna pozostaje nietknięta.

Kiedy zadzwonisz

Console.WriteLine(a);

to jest umieszczane w pudełku object, co konwertuje je na odwołanie o wartości null, które jest drukowane jako pusty wiersz.

Jon Skeet
źródło
3
Czy istnieje sposób na wydrukowanie „null” bez użycia Console.WriteLine(a == null ? "null" : a)?
Cole Johnson
5
@Cole Johnson: Console.WriteLine (a ?? "null"); :)
David Chappelle
2
@DavidChappelle Gdy ajest typu int?, powiedziałbym, a ?? "null"że nie znajduje wspólnego typu dla dwóch operandów. Potrzebujesz podpowiedzi, która objectjest powszechnym typem. Rzuć więc na to dowolny operand. aZostaną zapakowane. Na przykład ((object)a) ?? "null"lub a ?? (object)"null".
Jeppe Stig Nielsen
Wspaniały. czy możesz podać link do specyfikacji? czy możemy to zdefiniować, gdy przeciążamy operatory w naszych własnych klasach?
Phil
@teabaggs: Nie mogę teraz łatwo utworzyć linku, a link prowadziłby po prostu do dokumentu Worda (który można łatwo znaleźć za pomocą wyszukiwania). W razie potrzeby automatycznie uzyskujesz podniesione operatory po zdefiniowaniu przeciążenia operatorów.
Jon Skeet
116

Odpowiedź Jona jest poprawna, ale chciałbym dodać kilka dodatkowych uwag.

Dlaczego Console.WriteLine(null)podaje błąd kompilacji?

Istnieje 19 przeciążeń, Console.WriteLinea trzy z nich mają zastosowanie do a null: ten, który przyjmuje a string, ten, który przyjmuje a char[]i ten, który przyjmuje object. C # nie może określić, który z tych trzech elementów masz na myśli, więc wyświetla błąd. Console.WriteLine((object)null)byłoby legalne, ponieważ teraz jest jasne.

dlaczego Console.WriteLine(a)pisze pustą linię?

ajest null int?. Rozpoznanie przeciążenia wybiera objectwersję metody, więc int?jest opakowana w odwołanie o wartości null. Więc to jest w zasadzie to samo Console.WriteLine((object)null), co, co pisze pustą linię.

Dlaczego nie ma NullReferenceExceptionna podwyższeniu?

Gdzie jest zerowe odniesienie , o które się martwisz? ajest wartością null, int?która nie jest typem referencyjnym! Pamiętaj, że typy wartości dopuszczające wartość null są typami wartości , a nie typami odwołań , więc nie oczekuj, że będą miały semantykę odwołania, chyba że są opakowane w typ referencyjny. W dodatku nie ma boksu.

Eric Lippert
źródło
0

Czy zwiększasz wartość null ???

int? a = null;
a++;

To stwierdzenie oznacza po prostu null++np. Null + 1.

Zgodnie z tym dokumentem typ dopuszczający wartość null może reprezentować prawidłowy zakres wartości dla jego bazowego typu wartości oraz dodatkową wartość null. Do wartości Nullable, wymawianej jako „Nullable of Int32”, można przypisać dowolną wartość z zakresu od -2147483648 do 2147483647 lub można przypisać wartość null

Tutaj zwiększasz wartość null, a następnie stanie się wartością zerową, a nie 0 lub inną liczbą całkowitą.

Dlaczego drukuje puste zamiast błędu?

kiedy drukujesz typ dopuszczający wartość null z wartością null, drukuje on pusty zamiast błędu, ponieważ drukujesz zmienną, tj. wartość miejsca w pamięci. który może być null lub dowolną liczbą całkowitą.

Ale kiedy próbujesz wydrukować wartość null przy użyciu Console.WriteLine(null), ponieważ null nie jest zmienną, więc nie odnosi się do żadnej lokalizacji pamięci. I stąd daje błąd "NullReferenceException".

W takim razie jak można wydrukować dowolną liczbę całkowitą za pomocą Console.WriteLine(2);??

W tym przypadku 2 trafi do pamięci w tymczasowej lokalizacji, a wskaźnik wskazuje na to miejsce w pamięci do wydrukowania.

Laxmikant Dange
źródło