Podczas pisania jakiegoś szczególnie złożonego kodu obsługi wyjątków ktoś zapytał, czy nie musisz się upewnić, że obiekt wyjątku nie jest null? Odpowiedziałem, że oczywiście nie, ale zdecydowałem się spróbować. Najwyraźniej możesz zgłosić null, ale nadal jest gdzieś zamieniony w wyjątek.
Dlaczego jest to dozwolone?
throw null;
W tym fragmencie na szczęście „ex” nie jest zerowe, ale czy kiedykolwiek mogło tak być?
try
{
throw null;
}
catch (Exception ex)
{
//can ex ever be null?
//thankfully, it isn't null, but is
//ex is System.NullReferenceException
}
c#
exception-handling
cięty
źródło
źródło
throw
przypadku instrukcji, której celem jest przede wszystkim zgłoszenie wyjątku, ostrzeżenie nie dodaje dużej wartości.Odpowiedzi:
Ponieważ specyfikacja języka oczekuje tam wyrażenia typu
System.Exception
(w związku z tymnull
jest poprawna w tym kontekście) i nie ogranicza tego wyrażenia do wartości innej niż null. Ogólnie rzecz biorąc, nie ma możliwości wykrycia, czy wartość tego wyrażenia jest,null
czy nie. Musiałby rozwiązać problem zatrzymania. Środowisko wykonawcze i tak będzie musiało zająć sięnull
sprawą. Widzieć:Exception ex = null; if (conditionThatDependsOnSomeInput) ex = new Exception(); throw ex;
Mogliby oczywiście uczynić konkretny przypadek odrzucenia
null
dosłownego nieważnym, ale to niewiele by pomogło, więc po co marnować miejsce na specyfikację i zmniejszać spójność z niewielkimi korzyściami?Zastrzeżenie (zanim zostanie uderzony przez Erica Lipperta): To jest moja własna spekulacja na temat uzasadnienia tej decyzji projektowej. Oczywiście nie byłem na spotkaniu projektowym;)
Odpowiedź na drugie pytanie, czy zmienna wyrażenia przechwycona w klauzuli catch może kiedykolwiek mieć wartość null: Podczas gdy specyfikacja C # milczy na temat tego, czy inne języki mogą powodować
null
propagowanie wyjątku, definiuje sposób propagowania wyjątków:Bo
null
pogrubione stwierdzenie jest fałszywe. Tak więc, chociaż opiera się wyłącznie na tym, co mówi specyfikacja C #, nie możemy powiedzieć, że bazowe środowisko wykonawcze nigdy nie zwróci wartości null, możemy być pewni, że nawet jeśli tak jest, będzie to obsługiwane tylko przezcatch {}
klauzulę ogólną .W przypadku implementacji języka C # w interfejsie CLI możemy odwołać się do specyfikacji ECMA 335. Ten dokument definiuje wszystkie wyjątki, które CLI zgłasza wewnętrznie (z których żaden nie jest
null
) i wspomina, że obiekty wyjątków zdefiniowane przez użytkownika są generowane przezthrow
instrukcję. Opis tej instrukcji jest praktycznie identyczny zthrow
instrukcją C # (z wyjątkiem tego, że nie ogranicza typu obiektu doSystem.Exception
):Uważam, że są one wystarczające, aby stwierdzić, że wychwycone wyjątki nigdy nie są
null
.źródło
throw null;
.catch { }
klauzuli ogólnej .throw
argumentu, którego argument może być wartością zerową, nie oznacza to, żethrow null
musiałby być legalny. Kompilator może nalegać, abythrow
argument miał rozpoznawalny typ klasy. Wyrażenie takie jak(System.InvalidOperationException)null
powinno być poprawne w czasie kompilacji (wykonanie powinno spowodować aNullReferenceException
), ale nie oznacza to, że nietypowenull
powinno być dopuszczalne.Próba rzucenia
null
obiektu powoduje (całkowicie niepowiązany) wyjątek odwołania zerowego.Pytanie, dlaczego możesz rzucać,
null
jest jak pytanie, dlaczego możesz to zrobić:object o = null; o.ToString();
źródło
Zaczerpnięte stąd :
źródło
Chociaż może nie być możliwe zgłoszenie wartości null w C #, ponieważ rzut wykryje to i przekształci go w wyjątek NullReferenceException, JEST możliwe, aby otrzymać wartość null ... Tak się składa, że otrzymuję to teraz, co powoduje mój catch (który nie był Oczekiwanie, że `` ex '' ma wartość null), aby doświadczyć zerowego wyjątku odniesienia, który następnie powoduje śmierć mojej aplikacji (ponieważ był to ostatni przechwycenie).
Tak więc, chociaż nie możemy wyrzucić null z C #, zaświat może zgłosić wartość null, więc twój najbardziej zewnętrzny catch (Exception ex) lepiej przygotuj się na jego otrzymanie. Po prostu FYI.
źródło
Myślę, że może nie możesz - kiedy próbujesz rzucić null, nie może, więc robi to, co powinien w przypadku błędu, czyli rzuca zerowy wyjątek odwołania. Więc tak naprawdę nie wyrzucasz null, nie możesz wyrzucić null, co powoduje rzut.
źródło
Próba odpowiedzi „… na szczęście„ ex ”nie jest zerowa, ale czy kiedykolwiek mogłaby tak być?”:
Ponieważ prawdopodobnie nie możemy zgłosić wyjątków, które są zerowe, klauzula catch również nigdy nie będzie musiała przechwytywać wyjątku, który jest pusty. Tak więc ex nigdy nie może być zerowy.
Teraz widzę, że to pytanie faktycznie zostało już zadane .
źródło
throw null
. Ale to nie spowoduje zgłoszenia wyjątku, który jest zerowy . Zamiast tego, ponieważthrow null
próbuje wywołać metody w odwołaniu o wartości null, zmusza to następnie środowisko uruchomieniowe do wyrzucenia niezerowej instancjiNullReferenceException
.Pamiętaj, że wyjątek zawiera szczegółowe informacje o tym, gdzie został zgłoszony. Biorąc pod uwagę, że konstruktor nie ma pojęcia, gdzie ma zostać rzucony, ma sens tylko to, że metoda rzucania wprowadza te szczegóły do obiektu w miejscu, w którym znajduje się rzut. Innymi słowy, środowisko CLR próbuje wstrzyknąć dane do wartości null, co wyzwala NullReferenceException.
Nie jestem pewien, czy tak właśnie się dzieje, ale wyjaśnia to zjawisko.
Zakładając, że to prawda (i nie mogę wymyślić lepszego sposobu, aby ex był zerowy, niż rzucanie null;), oznaczałoby to, że ex nie może być zerowy.
źródło
W starszym C #:
Rozważ tę składnię:
public void Add<T> ( T item ) => throw (hashSet.Add ( item ) ? null : new Exception ( "The item already exists" ));
Myślę, że jest o wiele krótszy niż to:
public void Add<T> ( T item ) { if (!hashSet.Add ( item )) throw new Exception ( "The item already exists" ); }
źródło