Referensi objek tidak disetel ke instance objek. Mengapa .NET tidak menunjukkan objek mana yang `null`?

103

Mengenai pesan pengecualian tidak tertangani .NET ini:

Referensi objek tidak disetel ke contoh objek.

Mengapa NET tidak menunjukkan objek mana null?

Saya tahu bahwa saya dapat memeriksa nulldan mengatasi kesalahan tersebut. Namun, mengapa NET tidak membantu menunjukkan objek mana yang memiliki referensi null dan ekspresi mana yang memicu NullReferenceException?


sumber
2
Ketika ini terjadi, tulis ulang baris yang terjadi sehingga memeriksa setiap hasil yang mungkin untuk null terlebih dahulu - kemudian Anda akan tahu persis apa itu. Entah itu, atau memiliki debugger luar biasa Visual Studio terpasang, yang
menghentikan
5
Tidak juga, dia hanya bertanya mengapa kerangka .NET tidak membantu programmer untuk menunjukkan objek mana yang nol. Saya kira itu adalah penalti kinerja (Anda perlu refleksi). tapi saya juga tidak yakin.
bas
1
@bas: Meskipun itu benar, pertanyaannya agak menyesatkan karena harus menanyakan tentang "bagian dari ekspresi", bukan "objek". Itu juga menjelaskan mengapa refleksi belaka tidak akan membantu, tetapi beberapa informasi debug yang ekstensif akan diperlukan.
ATAU Mapper
4
Saya masih penasaran dengan jawabannya. Ini agak mirip dengan pengecualian .net tidak membantu menunjukkan kunci mana yang tidak ada dalam kamus. Juga, saya tidak mengerti para pengabdi pada pertanyaan itu.
bas
12
Mohon terminologi: Sebuah objek tidak pernah nol. Sebuah referensi obyek mungkin sekalipun. Tetapi referensi objek hanyalah sebuah lokasi di memori - bagaimana hal itu membantu Anda, kecuali jika Anda tetap memasang debugger?
Oskar Berggren

Jawaban:

169

(Untuk informasi tentang pembantu pengecualian baru di Visual Studio 2017 lihat akhir jawaban ini)


Pertimbangkan kode ini:

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

Ini akan membuang NullReferenceExceptiondi baris kedua dan Anda ingin tahu mengapa .NET tidak memberi tahu Anda bahwa itu sadalah nol ketika pengecualian dilemparkan.

Untuk memahami mengapa Anda tidak mendapatkan informasi itu, Anda harus ingat bahwa itu bukan C # source yang mengeksekusi melainkan IL:

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

Ini adalah callvirtopcode yang melempar NullReferenceExceptiondan melakukan itu ketika argumen pertama pada tumpukan evaluasi adalah referensi nol (yang dimuat menggunakan ldloc.0).

Jika .NET harus dapat mengetahui bahwa itu adalah sreferensi null, itu harus dalam beberapa cara melacak bahwa argumen pertama pada tumpukan evaluasi berasal dari formulir s. Dalam hal ini mudah bagi kita untuk melihat bahwa itu adalah snol tetapi bagaimana jika nilainya adalah nilai yang dikembalikan dari pemanggilan fungsi lain dan tidak disimpan dalam variabel apa pun? Bagaimanapun, jenis informasi ini bukanlah yang ingin Anda lacak di mesin virtual seperti mesin virtual .NET.


Untuk menghindari masalah ini, saya sarankan Anda melakukan pemeriksaan argumen null di semua panggilan metode publik (kecuali tentu saja Anda mengizinkan referensi null):

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

Jika null dilewatkan ke metode Anda mendapatkan pengecualian yang secara tepat menjelaskan apa masalahnya (yaitu snull).


Empat tahun kemudian Visual Studio 2017 sekarang memiliki pembantu pengecualian baru yang akan mencoba untuk memberitahu apa yang null ketika NullReferenceExceptiondilempar. Ia bahkan dapat memberi Anda informasi yang diperlukan jika itu adalah nilai kembalian dari metode yang nol:

Pembantu pengecualian Visual Studio 2017

Perhatikan bahwa ini hanya berfungsi dalam build DEBUG.

Martin Liversage
sumber
5
Nomor baris dan nama file sumber tidak disimpan dalam kode IL itu sendiri, atau benarkah? Namun, mereka dapat dibuat tersedia untuk debugging.
ATAU Mapper
4
@MartinLiversage: Tepat. Jadi pertanyaannya bermuara pada: Mengapa tidak ada cukup info yang disimpan dalam file simbol yang juga memberi tahu ekspresi apa dalam kode itu dievaluasi null.
ATAU Mapper
2
@MartinLiversage: File simbol dapat diakses sedemikian rupa sehingga dengan pengecualian, bersama dengan pesan pengecualian, file sumber dan nomor baris dapat ditampilkan dalam output debugger. Jadi, pertanyaannya adalah, apa alasan untuk tidak menyertakan beberapa info lebih lanjut tentang apa yang sebenarnya dikembalikan null- perhatikan bahwa OP tidak mengklaim dia ingin tahu bahwa untuk rilis build, juga, build debug mungkin sudah cukup.
ATAU Mapper
3
Hmm, dan kita tidak boleh lupa bahwa bukan IL yang benar-benar mengeksekusi, melainkan kode asli yang dibuat darinya saat runtime.
Oskar Berggren
2
@MartinLiversage: Tidak seorang pun dalam pertanyaan ini yang mengklaim kami ingin ini didukung dengan sempurna untuk build rilis. Bagaimanapun, saya tidak begitu melihat masalah dalam menghubungkan opcode IL yang menggunakan referensi objek (yang mungkin ternyata null) dengan baris dan kolom file sumber yang mengembalikan referensi objek tersebut.
ATAU Mapper
9

Seperti apa tampilan pesan kesalahan yang Anda inginkan dalam kasus berikut ini?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

Tidak ada nama variabel untuk dilaporkan di sini!

romar
sumber
2
Saya menduga OP sedang mencari ekspresi dalam kode sumber yang mengembalikan null, bukan objeknya. Saya telah menambahkan komentar masing-masing ke pertanyaan dan berharap dia akan menjelaskan semuanya. Jika kecurigaan saya benar, OP akan mengharapkan sesuatu seperti Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.pesan kesalahan.
ATAU Mapper
1
@ORMap Saya setuju. Saya hanya akan meletakkan "jawaban" saya dalam komentar ke OP, jika saya memiliki cukup poin reputasi untuk menambahkan komentar!
romar
1
"referensi null mencoba memanggil metode ToString () dari Kelas XYZ" akan lebih membantu daripada yang kita dapatkan sekarang.
Michael Levy
Hal yang paling membantu adalah pelacakan tumpukan yang menunjukkan, pada setiap tingkat panggilan, baris apa, dalam file apa, yang menyebabkan kesalahan. Oh, tunggu ... itulah yang dilakukannya sekarang!
Jim Balter
1

Nah, itu terserah insinyur di Microsoft untuk menjawab. Tapi Anda jelas dapat menggunakan debugger dan menambahkan jam tangan untuk mencari tahu mana yang bermasalah.

Namun, pengecualiannya NullReferenceExceptionberarti referensi tersebut tidak ada . Anda tidak bisa mendapatkan objek yang belum dibuat sama sekali.

but why .NET don't tell us which object is null? Karena tidak tahu objek mana yang nol. Objeknya sama sekali tidak ada!

Sama halnya ketika saya mengatakan, C # dikompilasi ke kode .NET IL. Kode .NET IL tidak mengetahui nama atau ekspresi. Ia hanya mengetahui referensi dan lokasinya. Di sini juga, Anda tidak bisa mendapatkan apa yang tidak ada. Ekspresi atau nama variabel tidak ada.

Filosofi: Anda tidak dapat membuat telur dadar jika Anda tidak memiliki telur.

Aniket Inge
sumber
3
itu juga bukan jawaban :)
bas
Bagaimana Anda mendapatkan referensi jika tidak ada? @Bas
Aniket Inge
4
"Nah, terserah insinyur di Microsoft untuk menjawab." Jadi biarkan mereka menjelaskan pada ini bukannya menyatakan yang sudah jelas
bas
@bas kita dapat memutuskan apa yang paling tidak logis. Secara logis objek tersebut tidak ada. Bagaimana Anda menangkapnya dengan pengecualian dan kemudian mencetak nama objeknya? Itu tidak ada. Bahkan tidak di tumpukan ..
Aniket Inge
jadi jawabannya adalah hampir tidak mungkin untuk menunjukkan objek mana yang memiliki referensi nol? Itu juga jawaban. Saya tidak menyatakan bahwa saya tahu, saya hanya suka pertanyaannya :). +1 untuk semua upaya: p
bas
1

Tidak yakin, tetapi ini mungkin karena .Net tidak tahu apakah itu kelas yang ditentukan sebelumnya atau ditentukan pengguna. Jika sudah ditentukan sebelumnya maka bisa nihil (seperti string yang menempati 2 Bytes) tetapi jika itu ditentukan oleh pengguna daripada kita harus membuat turunannya sehingga tahu bahwa objek ini akan menempati banyak memori ini. Jadi karena itu melempar kesalahan pada saat run-time.

oniel telies
sumber
-2

Pertanyaan bagus. Kotak pesan tidak berguna. Bahkan jika itu terkubur satu mil jauhnya dari definisi referensi, beberapa kelas atau perakitan atau file atau informasi lain akan lebih baik daripada yang mereka sediakan saat ini (baca: lebih baik daripada tidak sama sekali).

Pilihan terbaik Anda adalah menjalankannya di debugger dengan informasi debug, dan IDE Anda akan berhenti di baris yang menyinggung (cukup jelas menunjukkan bahwa informasi yang berguna sebenarnya tersedia).

Rick O'Shea
sumber