Mengapa "referensi objek tidak disetel ke instance objek" memberi tahu kami objek mana?

39

Kami meluncurkan sistem, dan terkadang kami mendapatkan pengecualian yang terkenal NullReferenceException dengan pesannya Object reference not set to an instance of an object.

Namun, dalam metode di mana kita memiliki hampir 20 objek, memiliki log yang mengatakan objek adalah null, benar-benar tidak berguna sama sekali. Ini seperti memberi tahu Anda, ketika Anda adalah agen keamanan sebuah seminar, bahwa seorang pria di antara 100 hadirin adalah seorang teroris. Itu benar-benar tidak berguna sama sekali bagi Anda. Anda harus mendapatkan informasi lebih lanjut, jika Anda ingin mendeteksi pria mana yang merupakan pria yang mengancam.

Demikian juga, jika kita ingin menghapus bug, kita perlu tahu objek mana yang null.

Sekarang, sesuatu telah terobsesi dengan pikiran saya selama beberapa bulan, dan itu adalah:

Mengapa .NET tidak memberi kami nama, atau setidaknya jenis referensi objek, yang nol?. Tidak bisakah ia memahami tipe dari refleksi atau sumber lain?

Juga, apa praktik terbaik untuk memahami objek mana yang nol? Haruskah kita selalu menguji nullability objek dalam konteks ini secara manual dan mencatat hasilnya? Apakah ada cara yang lebih baik?

Pembaruan: Pengecualian The system cannot find the file specifiedmemiliki sifat yang sama. Anda tidak dapat menemukan file mana, sampai Anda melampirkan ke proses dan debug. Saya kira jenis pengecualian ini bisa menjadi lebih cerdas. Bukankah lebih baik jika .NET dapat memberi tahu kami c:\temp.txt doesn't exist.alih-alih pesan umum itu? Sebagai pengembang, saya memilih ya.

Saeed Neamati
sumber
14
Pengecualian harus mencakup jejak tumpukan dengan nomor baris. Saya akan memulai investigasi saya dari sana melihat setiap objek yang diakses pada baris itu.
PersonalNexus
2
Juga, saya selalu bertanya-tanya mengapa dialog pembantu pengecualian di Visual Studio menyertakan petunjuk "membantu" yang digunakan newuntuk membuat instance kelas. Kapan petunjuk semacam itu benar-benar membantu?
PersonalNexus
1
Jika Anda memiliki rantai panggilan objek sepuluh maka Anda punya masalah dengan kopling dalam desain Anda.
Pete Kirkham
9
Saya suka bagaimana setiap jawaban untuk pertanyaan ini ada di sepanjang baris "Gunakan debugger, catat kesalahan Anda, periksa nol, itu kesalahan Anda" yang tidak menjawab pertanyaan tetapi menyalahkan Anda. Hanya di stackoverflow seseorang benar-benar memberi Anda jawaban (yang saya pikir mengatakan itu terlalu banyak biaya untuk VM untuk melacak). Tapi sungguh, satu-satunya orang yang dapat menjawab pertanyaan ini dengan benar adalah seseorang dari microsoft yang bekerja pada framework.
Rocklan

Jawaban:

28

Pada NullReferenceExceptiondasarnya memberitahu Anda: Anda salah melakukannya. Tidak lebih, tidak kurang. Sebaliknya, itu bukan alat debugging lengkap. Dalam hal ini saya katakan Anda salah melakukannya karena keduanya

  • ada NullReferenceException
  • Anda tidak mencegahnya dengan cara Anda tahu mengapa / di mana itu terjadi
  • dan mungkin juga: sebuah metode yang membutuhkan 20 objek sepertinya sedikit salah

Saya penggemar berat memeriksa semuanya sebelum segala sesuatunya mulai salah, dan memberikan informasi yang baik kepada pengembang. Singkatnya: tulis cek menggunakan ArgumentNullExceptiondan suka dan tulis nama sendiri. Berikut ini contohnya:

void Method(string a, SomeObject b)
{
    if (a == null) throw ArgumentNullException("a");
    if (b == null) throw ArgumentNullException("b");

    // See how nice this is, and what peace of mind this provides? As long as
    // nothing modifies a or b you can use them here and be 100% sure they're not
    // null. Should they be when entering the method, at least you know which one
    // is null.
    var c = FetchSomeObject();
    if(c == null)
    {
        throw InvalidOperationException("Fetching B failed!!");
    }

    // etc.
}

Anda juga dapat melihat Kontrak Kode , memiliki kebiasaan tetapi berfungsi dengan baik dan menghemat beberapa mengetik.

stijn
sumber
17
@ SaeedNeamati Anda tidak menyiratkan bahwa karena Anda memiliki basis kode yang besar, Anda seharusnya tidak melakukan pengecekan kesalahan yang layak, bukan? Jika semakin besar proyek, semakin penting peran atau kesalahan memeriksa dan melaporkan.
stijn
6
Beri +1 untuk saran yang baik, bahkan jika itu tidak menjawab pertanyaan yang sebenarnya (yang mungkin hanya Anders Hejlsberg yang bisa menjawab).
Ross Patterson
12
+1 untuk saran yang bagus. @ SaeedNeamati Anda harus mendengarkan saran ini. Masalah Anda disebabkan oleh kecerobohan dan kurangnya profesionalisme dalam kode. Jika Anda tidak punya waktu untuk menulis kode yang baik, Anda memiliki masalah yang lebih besar ...
MattDavey
3
Jika Anda tidak punya waktu untuk menulis kode yang baik , maka Anda pasti tidak punya waktu untuk menulis kode yang buruk . Menulis kode yang buruk membutuhkan waktu lebih lama daripada menulis kode yang baik. Kecuali Anda benar - benar tidak peduli dengan bug. Dan jika Anda benar - benar tidak peduli dengan bug, mengapa menulis program ini sama sekali?
MarkJ
5
@ SaeedNeamati I only say that checking every object to get sure that it's not null, is not a good methodSerius. Ini metode terbaik. Dan bukan hanya nol, periksa setiap argumen untuk nilai yang masuk akal. Semakin awal Anda menangkap kesalahan, semakin mudah untuk menemukan penyebabnya. Anda tidak ingin harus melacak beberapa tingkat dalam jejak tumpukan untuk menemukan panggilan yang menyebabkannya.
jgauffin
19

Itu benar-benar harus menunjukkan dengan tepat apa yang Anda coba panggil. Itu seperti mengatakan "Ada masalah. Kamu harus memperbaikinya. Aku tahu apa itu. Aku tidak akan memberitahumu. Kamu mencari tahu" Agak seperti setengah jawaban pada Stack Overflow ini, ironisnya.

Jadi seberapa bermanfaatkah itu, misalnya, jika Anda mendapatkan ini ...

Object reference (HttpContext.Current) not set to instance of an object

...? Harus masuk ke dalam kode, melangkah melaluinya, dan mencari tahu bahwa hal yang Anda coba panggil nullbaik-baik saja, tetapi mengapa tidak memberi kami sedikit bantuan?

Saya setuju bahwa biasanya berguna untuk menelusuri kode untuk mendapatkan jawaban (karena Anda mungkin akan menemukan lebih banyak), tetapi sering kali banyak waktu dan frustrasi akan disimpan jika NullReferenceExceptionteksnya lebih seperti contoh di atas.

Katakan saja.

LiverpoolsNumber9
sumber
Bagaimana runtime seharusnya tahu apa itu null?
Will
3
Runtime memiliki informasi lebih banyak daripada yang disediakannya. Apa pun info berguna yang dimilikinya, ia harus menyediakan. Itu saja yang saya katakan. Menanggapi pertanyaan Anda, mengapa tidak tahu? Jika Anda tahu jawabannya, Anda mungkin bisa memberikan komentar yang lebih baik daripada yang Anda miliki.
LiverpoolsNumber9
Setuju, dan saya akan mengatakan hal yang sama untuk KeyNotFoundExceptiondan banyak gangguan lainnya ...
sinelaw
Sebenarnya, dalam kasus null dereferencing ini tampak seperti batasan dalam cara dereferensi CLR (terima kasih atas komentar Shahrooz Jefri pada pertanyaan OP)
sinelaw
1
blogs.msdn.microsoft.com/dotnet/2016/08/02/... AKHIRNYA !!!
LiverpoolsNumber9
4

Log Anda harus menyertakan jejak stack - yang biasanya memberi Anda petunjuk tentang baris mana dalam metode yang memiliki masalah. Anda mungkin perlu membuat versi rilis Anda menyertakan simbol PDB sehingga Anda memiliki ide untuk baris mana kesalahan tersebut aktif.

Memang, itu tidak akan membantu Anda dalam hal ini:

Foo.Bar.Baz.DoSomething()

Prinsip tell don't ask dapat membantu menghindari kode tersebut.

Mengenai mengapa informasi tersebut tidak termasuk, saya tidak yakin - Saya menduga bahwa setidaknya dalam membangun debug, jika mereka benar-benar ingin, mereka bisa mengetahuinya. Mengambil crash dump dan membuka di WinDBG dapat membantu.

Paul Stovell
sumber
2

Pengecualian telah dibuat sebagai alat untuk menandai kondisi non-fatal yang luar biasa di rantai panggilan. Artinya, mereka tidak dirancang sebagai alat debugging.

Jika pengecualian null-pointer adalah alat debugging, itu akan membatalkan eksekusi program tepat di tempat, memungkinkan debugger untuk terhubung, mengarahkannya tepat di garis yang memberatkan. Ini akan memberi programmer semua informasi konteks yang tersedia. (Yang cukup banyak seperti Segfault karena akses pointer nol tidak dalam C, meskipun agak kasar.)

Namun, pengecualian null-pointer dirancang sebagai kondisi runtime yang valid yang dapat dibuang dan ditangkap dalam aliran program normal. Konsekuensinya, pertimbangan kinerja perlu diperhitungkan. Dan setiap kustomisasi pesan pengecualian membutuhkan objek string yang dibuat, digabungkan, dan dihancurkan saat runtime. Dengan demikian, pesan statis tidak dapat disangkal lebih cepat.

Saya tidak mengatakan bahwa runtime tidak dapat diprogram dengan cara yang akan menghasilkan nama referensi yang memberatkan. Itu bisa dilakukan. Itu hanya akan membuat pengecualian lebih lambat dari mereka. Jika ada yang cukup peduli, fitur semacam itu bahkan dapat dibuat dapat dipindah-pindah, sehingga tidak akan memperlambat kode produksi, tetapi memungkinkan proses debug yang lebih mudah; tetapi karena alasan tertentu tampaknya tidak ada yang cukup peduli.

cmaster
sumber
1

Cromulent mengenai kuku di kepala saya pikir, namun ada poin yang jelas juga bahwa jika Anda mendapatkan NullReferenceExceptionAnda memiliki variabel yang tidak diinisialisasi. Argumen bahwa Anda memiliki sekitar 20 objek yang diteruskan ke dalam metode tidak dapat dikatakan sebagai mitigasi: sebagai pencipta sejumlah kode Anda harus bertanggung jawab atas tindakannya, yang mencakup kepatuhannya pada seluruh basis kode, karena pemanfaatan variabel yang tepat dan benar, dll.

itu berat, membosankan dan kadang-kadang membosankan, tetapi hasilnya pada akhirnya sepadan: banyak kali ketika saya harus menjelajah melalui file log dengan berat beberapa gigabyte, dan mereka hampir selalu membantu. Namun sebelum Anda sampai ke tahap itu, debugger dapat membantu Anda, dan sebelum tahap perencanaan yang baik akan menghemat banyak rasa sakit (dan saya tidak bermaksud pendekatan rekayasa penuh untuk Anda solusi kode baik: sketsa sederhana dan beberapa catatan dapat dan akan lebih baik daripada tidak sama sekali).

Berkenaan dengan Object reference not set to an instance of an objectkode tidak dapat menebak nilai-nilai yang kita sukai: itu adalah tugas kami sebagai programmer, dan itu berarti Anda telah melewati variabel yang tidak diinisialisasi.

GMasucci
sumber
Anda sadar orang-orang biasa menawarkan pembenaran seperti ini untuk menulis dalam bahasa majelis? Dan tidak menggunakan pengumpulan sampah otomatis? Dan bisa melakukan aritmatika - dalam hex? "berat, membosankan, dan terkadang membosankan" adalah cara kami menggambarkan pekerjaan yang seharusnya otomatis.
Spike0xff
0

Pelajari cara menggunakan debugger. Ini persis seperti apa itu dirancang untuk. Tetapkan titik istirahat pada metode yang dimaksud dan pergilah.

Cukup langkah melalui kode Anda dan lihat apa nilai semua variabel Anda pada titik-titik tertentu.

Sunting: Terus terang saya terkejut bahwa belum ada orang lain yang menyebutkan menggunakan debugger.

Cromulen
sumber
2
Jadi Anda mengatakan bahwa semua pengecualian dapat dengan mudah direproduksi saat menggunakan debugger?
jgauffin
@ jgauffin Saya mengatakan bahwa Anda dapat menggunakan debugger untuk melihat mengapa pengecualian dilemparkan dalam kode dunia nyata daripada tes unit sintetis yang mungkin tidak benar-benar menguji kode yang dimaksud atau unit tes itu sendiri mungkin memiliki bug yang menyebabkan mereka ketinggalan bug dalam kode asli. Seorang debugger mengalahkan hampir semua alat lain yang dapat saya pikirkan (kecuali mungkin hal-hal seperti Valgrind atau DTrace).
Cromulent
1
Jadi Anda mengatakan bahwa kami selalu memiliki akses ke komputer yang rusak dan debugging lebih baik daripada tes unit?
jgauffin
@ jgauffin Saya katakan jika Anda punya pilihan kemudian pergi dengan debugger di preferensi untuk alat-alat lain. Tentu saja jika Anda tidak memiliki pilihan itu maka itu sedikit tidak starter. Jelas ini bukan masalah dengan pertanyaan ini yang mengapa saya memberikan jawaban yang saya lakukan. Jika pertanyaannya adalah bagaimana Anda memperbaiki masalah ini pada komputer klien tanpa cara melakukan debugging jarak jauh (atau debugging lokal) jawaban saya akan berbeda. Anda sepertinya mencoba melemahkan jawaban saya dengan cara yang tidak ada relevansinya dengan pertanyaan yang ada.
Cromulent
0

Jika Anda ingin menggunakan jawaban @ stijn dan memberikan cek nol pada kode Anda, cuplikan kode ini akan membantu. Berikut ini beberapa info tentang cuplikan kode . Setelah Anda mengatur ini, cukup ketik argnull, tekan tab dua kali, dan kemudian isi yang kosong.

<CodeSnippet Format="1.0.0">
  <Header>
    <Title>EnsureArgNotNull</Title>
    <Shortcut>argnull</Shortcut>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>argument</ID>
        <ToolTip>The name of the argument that shouldn't be null</ToolTip>
        <Default>arg</Default>
      </Literal>
    </Declarations>
    <Code Language="CSharp">
      <![CDATA[if ($argument$ == null) throw new ArgumentNullException("$argument$");$end$]]>
    </Code>
  </Snippet>
</CodeSnippet>
pengguna2023861
sumber
note: c # 6 sekarang sudah nameofjadi snippet Anda bisa jadi throw new ArgumentNullException(nameof($argument$))yang memiliki keuntungan tidak termasuk konstanta sihir, diperiksa oleh kompiler, dan bekerja lebih baik dengan alat refactoring
stijn