Katakanlah saya punya metode:
public void DoSomething(ISomeInterface someObject)
{
if(someObject == null) throw new ArgumentNullException("someObject");
someObject.DoThisOrThat();
}
Saya telah dilatih untuk percaya bahwa melempar ArgumentNullException
is "benar" tetapi "Referensi objek tidak disetel ke instance objek" kesalahan berarti saya memiliki bug.
Mengapa?
Saya tahu bahwa jika saya menyimpan referensi ke someObject
dan menggunakannya nanti, maka lebih baik untuk memeriksa nullity ketika diteruskan, dan gagal lebih awal. Namun, jika saya melakukan dereferensi pada baris berikutnya, mengapa kita harus melakukan pemeriksaan? Ini akan melempar pengecualian dengan satu atau lain cara.
Edit :
Itu baru saja terpikir oleh saya ... apakah ketakutan akan null dereferensi berasal dari bahasa seperti C ++ yang tidak memeriksa Anda (yaitu hanya mencoba menjalankan beberapa metode di lokasi memori nol + metode offset)?
Jawaban:
Saya kira jika Anda segera mereferensikan variabel, Anda bisa berdebat dengan cara apa pun, tapi saya masih lebih suka ArgumentNullException.
Ini jauh lebih eksplisit tentang apa yang sedang terjadi. Pengecualian berisi nama variabel yang null, sedangkan NullReferenceException tidak. Khususnya di tingkat API, ArgumentNullException memperjelas bahwa beberapa penelepon tidak menghormati kontrak suatu metode, dan kegagalan itu tidak selalu merupakan kesalahan acak atau beberapa masalah yang lebih dalam (meskipun itu mungkin masih terjadi). Anda harus gagal lebih awal.
Selain itu, apa yang nantinya cenderung dilakukan oleh pengelola? Tambahkan ArgumentNullException jika mereka menambahkan kode sebelum dereference pertama, atau cukup tambahkan kode dan kemudian izinkan NullReferenceException dilemparkan?
sumber
Misalkan saya memanggil metode
M(x)
yang Anda tulis. Saya lulus nol. Saya mendapatkan ArgumentNullException dengan nama diatur ke "x". Pengecualian itu jelas berarti saya memiliki bug; Saya seharusnya tidak melewati nol untuk x.Misalkan saya memanggil metode M yang Anda tulis. Saya lulus nol. Saya mendapatkan pengecualian deref nol. Bagaimana saya bisa tahu apakah saya memiliki bug atau Anda memiliki bug atau perpustakaan pihak ketiga yang Anda panggil atas nama saya memiliki bug? Saya tidak tahu apa-apa apakah saya perlu memperbaiki kode saya atau menelepon Anda untuk memberi tahu Anda untuk memperbaiki kode Anda.
Kedua pengecualian merupakan indikasi bug; tidak harus pernah dilempar dalam kode produksi. Pertanyaannya adalah pengecualian mana yang mengkomunikasikan siapa yang memiliki bug lebih baik kepada orang yang melakukan debugging? Pengecualian Null deref hampir tidak memberi tahu Anda; argumen null exception memberi tahu Anda banyak hal. Bersikap baik kepada pengguna Anda; melemparkan pengecualian yang memungkinkan mereka belajar dari kesalahan mereka.
sumber
"neither should ever be thrown in production code"
jenis kode / situasi apa yang akan mereka gunakan? Haruskah mereka dikompilasi sepertiDebug.Assert
? Atau maksud Anda jangan membuangnya dari API?throw new ArgumentNullException
kode produksi Anda, dan seharusnya tidak pernah berjalan di luar test case . Pengecualian seharusnya tidak benar-benar dibuang karena penelepon tidak boleh memiliki bug itu.Intinya, kode Anda harus melakukan sesuatu yang bermakna.
A
NullReferenceException
tidak terlalu bermakna, karena bisa berarti segalanya. Tanpa melihat di mana pengecualian dilemparkan (dan kode itu mungkin tidak tersedia bagi saya) saya tidak tahu, apa yang salah.An
ArgumentNullException
bermakna, karena memberi tahu saya: operasi gagal karena argumensomeObject
itunull
. Saya tidak perlu melihat kode Anda untuk mencari tahu.Juga meningkatkan pengecualian ini berarti, bahwa Anda secara eksplisit menangani
null
, meskipun satu-satunya cara Anda untuk melakukannya adalah dengan mengatakan, bahwa Anda tidak dapat menanganinya, sementara hanya membiarkan kode crash berpotensi berarti bahwa, Anda mungkin benar-benar dapat menangani null , tetapi lupa mengimplementasikan kasus ini.Tujuan pengecualian adalah untuk mengkomunikasikan informasi jika terjadi kegagalan, yang dapat ditangani pada saat runtime untuk pulih dari kegagalan, atau pada waktu debug untuk lebih memahami apa yang salah.
sumber
Saya percaya satu-satunya keuntungan adalah Anda dapat memberikan diagnosa yang lebih baik dalam pengecualian. Itu Anda tahu, bahwa pemanggil fungsi lewat nol, sementara jika Anda membiarkan dereference membuangnya, Anda tidak akan yakin apakah pemanggil melewati data yang salah atau ada bug dalam fungsi itu sendiri.
Saya akan melakukannya jika orang lain akan memanggil fungsi untuk membuatnya lebih mudah digunakan (men-debug jika terjadi kesalahan) dan tidak melakukannya dalam kode internal saya, karena runtime akan memeriksanya.
sumber
Anda memiliki bug jika kode tidak berfungsi seperti yang dirancang. Sebuah
NullReferenceException
dilemparkan oleh sistem dan fakta itu benar-benar membuat lebih banyak bug daripada fitur. Bukan hanya karena Anda tidak menetapkan pengecualian khusus untuk ditangani. Juga karena pengembang membaca kode Anda mungkin menganggap Anda tidak memperhitungkan situasi yang terjadi.Untuk alasan yang sama bahwa
ApplicationException
milik aplikasi Anda vs seorang jenderalException
yang milik sistem operasi. Jenis pengecualian yang dilemparkan menunjukkan dari mana asalnya pengecualian.Jika Anda telah menghitung nol, lebih baik Anda menanganinya dengan anggun dengan melemparkan pengecualian tertentu atau jika itu merupakan input yang dapat diterima, maka jangan melempar pengecualian karena harganya mahal. Tangani dengan melakukan operasi dengan standar yang baik atau tanpa operasi sama sekali (mis. Tidak diperlukan pembaruan).
Akhirnya, jangan abaikan kemampuan penelepon untuk menangani situasi. Dengan memberi mereka pengecualian yang lebih spesifik,
ArgumentException
mereka dapat membuat percobaan / tangkapan mereka sedemikian rupa untuk menangani kesalahan ini sebelum yang lebih umumNullReferenceException
yang mungkin disebabkan oleh sejumlah alasan lain yang terjadi di tempat lain, seperti kegagalan untuk menginisialisasi variabel konstruktor.sumber
Pemeriksaan membuatnya lebih eksplisit apa yang terjadi, tetapi masalah sebenarnya datang dengan kode seperti contoh ini (dibuat-buat):
Di sini ada rantai panggilan yang terlibat dan tanpa cek nol Anda hanya melihat kesalahan di someOtherObject dan bukan di mana masalah pertama kali terungkap dengan sendirinya - yang secara instan berarti waktu terbuang untuk debugging atau menafsirkan tumpukan panggilan.
Secara umum lebih baik konsisten dengan hal semacam ini dan selalu menambahkan cek nol - yang membuatnya lebih mudah dikenali jika ada yang hilang daripada memilih dan memilih berdasarkan kasus per kasus (dengan asumsi Anda tahu bahwa nol adalah selalu tidak valid).
sumber
Alasan lain untuk dipertimbangkan adalah bagaimana jika beberapa pemrosesan terjadi sebelum objek nol digunakan?
Katakanlah Anda memiliki file data ID perusahaan terkait (Cid) dan ID orang (Pid). Ini disimpan sebagai aliran pasangan nomor:
Cid Pid Cid Pid Cid Pid Cid Pid Cid Pid
Dan fungsi VB ini menulis catatan ke file:
Jika
Person
merupakan referensi nol, baris tersebutFile.Write(Person.ID)
akan mengeluarkan pengecualian. Perangkat lunak dapat menangkap pengecualian dan pulih, tetapi ini menyisakan masalah. ID perusahaan telah ditulis tanpa ID orang yang terkait. Jika kenyataannya, saat Anda memanggil fungsi ini selanjutnya, Anda akan meletakkan ID perusahaan di mana ID orang sebelumnya diharapkan. Selain catatan yang salah, setiap catatan selanjutnya akan memiliki ID orang diikuti oleh ID perusahaan, yang merupakan jalan yang salah.Cid Pid Cid Pid Cid Cid Pid Cid Pid Cid
Dengan memvalidasi parameter di awal fungsi, Anda mencegah segala proses terjadi ketika metode dijamin gagal.
sumber