Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu. Dlaczego .NET nie pokazuje, który obiekt ma wartość „null”?


103

Odnośnie tego komunikatu o nieobsługiwanym wyjątku .NET:

Odwołanie do obiektu nie jest ustawione na wystąpienie obiektu.

Dlaczego .NET nie pokazuje, który obiekt jest null?

Wiem, że mogę sprawdzić nulli usunąć błąd. Jednak dlaczego .NET nie pomaga wskazać, który obiekt ma odwołanie o wartości null i które wyrażenie wyzwoliło NullReferenceException?

c#  .net 

2
Kiedy tak się stanie, przepisz wiersz, w którym się wydarzyło, aby najpierw sprawdzał każdy możliwy wynik pod kątem wartości null - wtedy będziesz dokładnie wiedzieć, co to było. Albo to, albo nie Visual Studio niesamowite debugger przyłączone, który rozkłada moment wystąpi wyjątek i pozwala zobaczyć co jest null :)
Patashu

5
Niezupełnie, po prostu pyta, dlaczego platforma .NET nie pomaga programiście w pokazaniu, który obiekt jest pusty. Myślę, że to spadek wydajności (potrzebowałbyś refleksji). ale też nie jestem pewien.
bas

1
@bas: To prawda, ale pytanie jest nieco mylące, ponieważ powinno dotyczyć „części wyrażenia”, a nie „obiektu”. To wyjaśnia również, dlaczego zwykła refleksja nie pomoże, ale wymagane będą obszerne informacje o debugowaniu.
LUB Mapper

4
Wciąż jestem ciekawa odpowiedzi. To trochę podobne do wyjątków .net, które nie pomagają wskazać, który klucz nie istnieje w słowniku. Nie rozumiem też wielbicieli tego pytania.
bas

12
Proszę o terminologię: obiekt nigdy nie jest pusty. Odwołanie do obiektu może być jednak. Ale odwołanie do obiektu to tylko lokalizacja w pamięci - jak by ci to pomogło, jeśli i tak nie masz dołączonego debugera?
Oskar Berggren

Odpowiedzi:


169

(Aby uzyskać informacje o nowym pomocniku wyjątków w programie Visual Studio 2017, zobacz koniec tej odpowiedzi)


Rozważ ten kod:

String s = null;
Console.WriteLine(s.Length);

Spowoduje to wrzucenie znaku NullReferenceExceptionw drugiej linii i chcesz wiedzieć, dlaczego .NET nie powie Ci, że był sto null, kiedy wyjątek został zgłoszony.

Aby zrozumieć, dlaczego nie dostajesz tej informacji, powinieneś pamiętać, że to nie źródło C # jest wykonywane, ale raczej IL:

IL_0001: ldnull      
IL_0002: stloc.0 // s
IL_0003: ldloc.0 // s
IL_0004: callvirt System.String.get_Length
IL_0009: wywołaj System.Console.WriteLine

Jest to callvirtkod operacji, który rzuca NullReferenceExceptioni robi to, gdy pierwszy argument na stosie wartościowania jest odwołaniem zerowym (tym, który został załadowany przy użyciu ldloc.0).

Jeśli .NET miałby być w stanie stwierdzić, sże było to odwołanie zerowe, powinien w jakiś sposób śledzić, że pierwszy argument na stosie ocen pochodzi z formularza s. W tym przypadku łatwo nam zauważyć, że jest sto wartość zerowa, ale co by było, gdyby wartość była wartością zwracaną z innego wywołania funkcji i nie była przechowywana w żadnej zmiennej? W każdym razie tego rodzaju informacje nie są tym, co chcesz śledzić na maszynie wirtualnej, takiej jak maszyna wirtualna .NET.


Aby uniknąć tego problemu, sugeruję, abyś sprawdzał argumenty null we wszystkich wywołaniach metod publicznych (chyba że oczywiście pozwolisz na zerowe odwołanie):

public void Foo(String s) {
  if (s == null)
    throw new ArgumentNullException("s");
  Console.WriteLine(s.Length);
}

Jeśli do metody zostanie przekazana wartość null, otrzymasz wyjątek, który dokładnie opisuje, na czym polega problem (czyli sjest zerowy).


Cztery lata później program Visual Studio 2017 ma teraz nowego pomocnika wyjątków, który spróbuje określić, co jest null, gdy NullReferenceExceptionzostanie zgłoszony. Jest nawet w stanie podać wymagane informacje, gdy jest to wartość zwracana metody, która ma wartość null:

Pomocnik wyjątków programu Visual Studio 2017

Zauważ, że działa to tylko w kompilacji DEBUG.


5
Numery wierszy i nazwy plików źródłowych też nie są przechowywane w samym kodzie IL, czy też tak jest? Jednak można je udostępnić do debugowania.
LUB Mapper

4
@MartinLiversage: Dokładnie. Tak więc pytanie sprowadza się do: dlaczego w plikach symboli nie ma wystarczającej ilości informacji, które również mówią, do jakiego wyrażenia w kodzie zostało obliczone null.
LUB Mapper

2
@MartinLiversage: Pliki symboli są dostępne w taki sposób, że w przypadku wyjątku, wraz z komunikatem o wyjątku, plik źródłowy i numer wiersza mogą być wyświetlane w danych wyjściowych debugera. Zatem pytanie brzmi, jaki jest powód, dla którego nie uwzględniono więcej informacji o tym, co dokładnie zwróciło null- zauważ, że OP nie twierdzi, że chce wiedzieć, że również w przypadku kompilacji wydań wersje debugowania mogą być wystarczające.
LUB Mapper

3
Hmm, i nie powinniśmy zapominać, że faktycznie nie jest wykonywana nawet IL, ale raczej natywny kod zbudowany z niej w czasie wykonywania.
Oskar Berggren

2
@MartinLiversage: Nikt w tym pytaniu nie twierdzi, że chcemy, aby było to doskonale obsługiwane w kompilacjach wydań. W każdym razie nie widzę problemu w korelowaniu kodu operacji IL, który używa odwołania do obiektu (który może się okazać null), z wierszem i kolumną pliku źródłowego, które zwróciły to odwołanie do obiektu.
LUB Mapper

9

Jak chcesz, aby komunikat o błędzie wyglądał w poniższym przypadku?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

Brak nazw zmiennych do raportowania tutaj!


2
Podejrzewam, że OP szuka w kodzie źródłowym wyrażenia, które zwraca wartość null, a nie obiekt. Dodałem odpowiedni komentarz do pytania i mam nadzieję, że on lub ona wyjaśni sprawę. Jeśli moje podejrzenie jest słuszne, OP spodziewałby się czegoś takiego Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.jak komunikat o błędzie.
LUB Mapper

1
@ORMapper Zgadzam się. Po prostu umieściłbym swoją „odpowiedź” w komentarzu do PO, gdybym miał wystarczająco dużo punktów reputacji, aby dodać komentarz!
romar

1
„odwołanie zerowe próbowało wywołać metodę ToString () klasy XYZ” byłoby bardziej pomocne niż to, co otrzymujemy teraz.
Michael Levy

Najbardziej pomocną rzeczą byłby ślad stosu pokazujący, na każdym poziomie wywołania, dokładnie, w której linii, w jakim pliku wystąpił błąd. Och, czekaj ... to właśnie robi teraz!
Jim Balter

1

Cóż, to zależy od inżynierów firmy Microsoft. Ale oczywiście możesz użyć debugera i dodać zegarek, aby dowiedzieć się, który z nich ma problem.

Jednak wyjątek stanowi, NullReferenceExceptionco oznacza, że ​​odniesienie nie istnieje . Nie możesz pobrać obiektu, który w ogóle nie został utworzony.

but why .NET don't tell us which object is null? Ponieważ nie wie, który obiekt jest pusty. Przedmiot po prostu nie istnieje!

To samo jest w przypadku, gdy powiem, że C # jest kompilowany do kodu .NET IL. Kod .NET IL nie zna nazw ani wyrażeń. Zna tylko referencje i ich lokalizację. Tutaj również nie możesz dostać tego, czego nie ma. Wyrażenie lub nazwa zmiennej nie istnieje.

Filozofia: nie możesz zrobić omletu, jeśli nie masz jajka.


3
to też nie jest odpowiedź :)
bas

Jak uzyskać odniesienie, jeśli nie istnieje? @Bas
Inge

4
„Cóż, to zależy od inżynierów firmy Microsoft”. Więc pozwól im rzucić trochę światła na to, zamiast mówić oczywiste
bas

@bas możemy zdecydować, co jest przynajmniej logiczne. Logicznie rzecz biorąc, obiekt nie istnieje. Jak wyłapiesz to z wyjątkiem, a następnie wydrukujesz nazwę obiektu? Po prostu nie istnieje. Nawet na stosie ...
Aniket Inge,

więc odpowiedź brzmi: praktycznie niemożliwe jest wskazanie, który obiekt ma odniesienie zerowe? To też jest odpowiedź. Nie twierdzę, że wiem, po prostu podoba mi się pytanie :). +1 za cały wysiłek: p
bas

1

Nie jestem pewien, ale może to być spowodowane tym, że .Net nie wie, czy jest to klasa predefiniowana, czy zdefiniowana przez użytkownika. Jeśli jest predefiniowany, może być zerowy (jak ciąg zajmujący 2 bajty), ale jeśli jest zdefiniowany przez użytkownika, musimy utworzyć jego instancję, aby wiedział, że ten obiekt zajmie tyle pamięci. Dlatego generuje błąd w czasie wykonywania.


-2

Dobre pytanie. Pole wiadomości jest po prostu bezużyteczne. Nawet jeśli jest zagrzebany milę głęboko od definicji referencji, jakaś klasa, zbiór, plik lub inne informacje byłyby lepsze niż to, co obecnie dostarczają (czytaj: lepsze niż nic).

Najlepszą opcją jest uruchomienie go w debugerze z informacjami debugującymi, a twoje IDE zepsuje się w linii powodującej błąd (raczej jasno pokazując, że przydatne informacje są w rzeczywistości dostępne).

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.