Haruskah layanan membuang pengecualian atau kembali ketika tidak ada item yang ditentukan untuk dihapus

12

Saya memiliki kode yang dapat direpresentasikan sebagai:

public class ItemService {

    public void DeleteItems(IEnumerable<Item> items)
    {
        // Save us from possible NullReferenceException below.
        if(items == null)
            return;

        foreach(var item in items)
        {
            // For the purpose of this example, lets say I have to iterate over them.
            // Go to database and delete them.
        }
    }
}

Sekarang saya bertanya-tanya apakah ini pendekatan yang tepat atau haruskah saya membuang pengecualian. Saya dapat menghindari pengecualian, karena kembali akan sama dengan mengulangi koleksi kosong, artinya, tidak ada kode penting yang dieksekusi, tetapi di sisi lain saya mungkin menyembunyikan masalah di suatu tempat dalam kode, karena mengapa ada orang yang ingin memanggil DeleteItemsdengan nullparameter? Ini mungkin menunjukkan bahwa ada masalah di tempat lain dalam kode.

Ini adalah masalah yang biasanya saya miliki dengan metode dalam layanan, karena kebanyakan dari mereka melakukan sesuatu dan tidak mengembalikan hasilnya, jadi jika seseorang memberikan informasi yang tidak valid maka tidak ada yang dapat dilakukan oleh layanan, sehingga ia kembali.

FCin
sumber
2
Ini hanya pendapat orang saya, tetapi saya selalu merasa paling masuk akal bahwa suatu metode harus mengeluarkan pengecualian ketika melakukan sesuatu yang tidak diinginkan (luar biasa). Dalam kasus Anda, saya akan melempar InvalidOperationException jika seseorang mencoba menghapus nol / 0 item.
Falgantil
1
@gnat Pertanyaan saya secara khusus tentang layanan, karena bagaimana mereka digunakan dan tujuannya. "Duplikat" ini hanya berbicara tentang kasus-kasus umum dan jawaban utama bahkan bertentangan dengan konteks metode saya yang sebenarnya.
FCin
2
Apakah mungkin enumerable menjadi kosong, bukan nol? Apakah Anda akan menanganinya secara berbeda?
JAD
13
@ Falgantil Saya bisa melihat null layak pengecualian, tapi saya pikir itu tidak masuk akal untuk melemparkan pengecualian pada daftar kosong.
Kevin

Jawaban:

58

Ini adalah dua pertanyaan yang berbeda.

Haruskah Anda menerima null? Itu tergantung pada kebijakan umum Anda tentang nullbasis kode. Menurut pendapat saya, melarang di nullmana-mana kecuali di mana didokumentasikan secara eksplisit adalah praktik yang sangat baik, tetapi praktik yang lebih baik untuk tetap berpegang pada konvensi yang sudah dimiliki basis kode Anda.

Haruskah Anda menerima koleksi kosong? Menurut pendapat saya: YA, tentu saja. Ini jauh lebih banyak upaya untuk membatasi semua penelepon ke koleksi non-kosong daripada melakukan hal yang benar secara matematis - bahkan jika itu mengejutkan beberapa pengembang yang ragu dengan konsep nol.

Kilian Foth
sumber
9
Bagaimanapun, jika Anda akan melempar, maka tidak ada banyak utilitas dalam melempar AhaINoticedYouPassingInNullExceptionketika runtime sudah memberi Anda fasilitas melempar NullReferenceExceptionuntuk Anda, tanpa Anda menulis kode sama sekali. Ada beberapa utilitas (misalnya alat cakupan kode Anda akan memberi tahu Anda apakah Anda memiliki unit test atau tidak untuk memasukkan null pada kasus sebelumnya atau tidak), tetapi tidak ada pengecualian yang harus dicoba / ditangkap pada setiap panggilan ke layanan, karena biasanya kesalahan programmer.
Steve Jessop
2
... Anda seharusnya hanya segera menangkap pengecualian yang muncul karena parameter yang tidak jelas, jika Anda tahu bahwa apa yang Anda sampaikan adalah nol dalam beberapa kasus yang diharapkan, dan Anda secara aktif menginginkan fungsi yang Anda panggil untuk memvalidasinya untuk Anda. Seperti yang dikatakan Kilian dalam jawabannya, kebijakan umum Anda mungkin melarang nol di mana pun itu tidak diizinkan secara eksplisit. Maka Anda tidak akan menangkap NullReferenceExceptionatau di AhaINoticedYouPassingInNullExceptionmana pun kecuali pada kode pemulihan bencana tingkat tinggi. Dan penangan pengecualian itu mungkin harus membuat laporan bug :-)
Steve Jessop
2
@FCin: desain antarmuka Anda harus sesuai dengan apa yang sebenarnya dibutuhkan pengguna dari layanan. Jadi, jika setiap penelepon akan mencoba / menangkap ini, maka metode ini atau metode kenyamanan di sampingnya harus memeriksa dan mengabaikan nol, karena itulah yang dituntut publik Anda. Namun, seperti dikatakan Kilian, biasanya berfungsi lebih baik untuk memperlakukan propagasi nol sebagai kesalahan pemrograman, dan tidak mencoba untuk melanjutkan di setiap situs panggilan. Dalam hal ini, bagaimana jika layanan tempat metode ini dipanggil null- apakah pengendali mengandung pelat ketel untuk menangkap dan melanjutkan dalam kasus itu juga?
Steve Jessop
2
... jika demikian, maka sepertinya basis kode secara konsisten memperlakukan komponen yang hilang sebagai kondisi yang diharapkan yang harus Anda tangani pada level rendah dan melanjutkan. Anda dapat dengan wajar bertanya, "siapa yang bertanggung jawab menyediakan layanan dan berhitung sehingga operasi ini dapat dilanjutkan?". Jika jawabannya adalah "seseorang", maka fungsi Anda memeriksa prasyarat , dan hasil prasyarat yang gagal biasanya akan menjadi pengecualian yang tertangkap di tingkat tinggi, bukan pada setiap panggilan. Jika hal-hal yang diperlukan untuk operasi benar-benar opsional, maka fungsi Anda menangani kasus penggunaan yang diharapkan .
Steve Jessop
2
ArgumentNullExceptionMelempar secara eksplisit lebih baik daripada membiarkan kode interior untuk melemparkan NullReferenceException- murni melihat pesan pengecualian, jelas bahwa itu adalah kesalahan input daripada kesalahan logika dalam tubuh metode di situs melempar, dan memastikan keluar awal berarti bahwa Anda lebih cenderung meninggalkan data lain dalam keadaan valid daripada bermutasi sebagian dari nol yang tidak terduga nantinya.
Miral
9

Nilai Null

Seperti yang sudah dikatakan @KilianFoth, patuhi kebijakan umum Anda. Jika itu dianggap nullsebagai "singkatan" untuk daftar kosong, lakukan seperti itu.

Jika Anda tidak memiliki kebijakan nullnilai yang konsisten , saya akan merekomendasikan yang berikut:

nullharus disediakan untuk mewakili situasi yang tidak dapat diekspresikan oleh tipe "normal", misalnya menggunakan nulluntuk mewakili "Saya tidak tahu". Dan itu adalah pilihan yang baik, karena semua orang yang mencoba menggunakan nilai ini secara sembarangan akan mendapatkan pengecualian, yang merupakan hal yang benar.

Menggunakan nullsebagai singkatan untuk daftar kosong tidak memenuhi syarat seperti itu, karena sudah ada representasi yang sempurna, menjadi daftar dengan nol elemen. Dan ini adalah pilihan yang secara teknis buruk, karena memaksa setiap bagian dari kode Anda berurusan dengan daftar untuk memeriksa singkatan yang valid null.

Daftar Kosong

Untuk suatu DeleteItems()metode, melewati daftar kosong secara efektif berarti tidak melakukan apa-apa. Saya akan membiarkan itu sebagai argumen, tidak melempar pengecualian, hanya kembali dengan cepat.

Tentu saja, penelepon dapat memeriksa elemen nol terlebih dahulu dan melewatkan DeleteItems()panggilan dalam kasus itu. Jika kita berbicara tentang API web, untuk alasan efisiensi pemanggil harus melakukan itu untuk menghindari lalu lintas yang tidak perlu dan latensi pulang-pergi. Tapi saya tidak berpikir API Anda harus menegakkan itu.

Ralf Kleberhoff
sumber
1

Melempar pengecualian dan menangani nulls dalam kode panggilan.

Sebagai aturan desain, cobalah untuk menghindari null sebagai nilai parameter. Ini akan mengurangi NullPointerExceptions secara umum, karena nol akan benar-benar menjadi pengecualian.

Selain itu, lihat sisa kode Anda. Jika ini adalah pola umum dalam proyek Anda maka tetaplah konsisten.

serprime
sumber
Saya berada di tengah "membentuk" bagaimana layanan saya disusun, sehingga beberapa refactoring mungkin diperlukan untuk memasukkan input yang tidak valid, tetapi tidak banyak. Tapi saya setuju dengan poin Anda bahwa nol akan menjadi pengecualian begitu saya mulai melempar bukannya menyembunyikannya.
FCin
0

Secara umum, pengecualian melempar harus disediakan untuk situasi luar biasa, yaitu jika kode tidak memiliki tindakan yang wajar untuk dilakukan dalam konteks saat ini.

Anda dapat menerapkan proses pemikiran itu dalam situasi ini. Mengingat ini adalah kelas publik dengan metode publik, Anda memiliki API publik, dan secara teori tidak ada kontrol atas apa yang diteruskan ke sana.

Kode Anda tidak memiliki konteks tentang apa yang memanggilnya (sebagaimana seharusnya), dan tidak ada yang dapat Anda lakukan dengan nilai nol. Ini tentu akan menjadi kandidat untuk ArgumentNullException.

richzilla
sumber
+ msgstr "jika kode tidak memiliki tindakan yang masuk akal untuk dilakukan dalam konteks saat ini". Tetapi pemikiran saya adalah, ia memiliki tindakan yang masuk akal, yaitu mengembalikan kekosongan. Itu melakukan hal yang persis sama seperti ketika melewati koleksi kosong, jadi jika saya mengizinkan koleksi kosong maka mengapa saya tidak mengizinkan null.
FCin
1
@FCin Karena koleksi kosong Anda tahu itu kosong. Dengan nullAnda tidak memiliki koleksi. Jika kode panggilan diharapkan / diharapkan untuk memasok koleksi ke metode, ada sesuatu yang salah, jadi Anda harus melempar. Satu-satunya alasan untuk memperlakukan null sama dengan koleksi kosong adalah jika Anda dapat secara wajar mengharapkan kode panggilan untuk menghasilkan koleksi null. Seperti jawaban lain yang dicatat, sebagian besar waktu, sesuatu nulladalah anomali. Jika Anda memperlakukan ini sama seperti koleksi kosong, Anda berpotensi mengabaikan masalah di tempat lain dalam kode.
JAD
@ CFin: juga, jika Anda menganggap nullkosong, itu khusus konteks untuk metode ini. Di tempat lain dalam basis kode yang sama, Anda mungkin memiliki IEnumerableatau IContainerparameter yang melakukan semacam penyaringan, nullmungkin berarti "tidak ada filter" (izinkan semuanya) sedangkan filter kosong berarti tidak boleh membiarkan apa pun. Dan di tempat lain, nullmungkin secara eksplisit berarti "Saya tidak tahu". Jadi, mengingat itu nulltidak selalu berarti hal yang sama di mana-mana, itu ide yang baik untuk tidak menciptakan makna sama sekali kecuali jika makna itu diperlukan atau setidaknya berguna bagi seseorang.
Steve Jessop
0

Pertanyaan ini tampaknya bukan tentang pengecualian, tetapi tentang nullmenjadi argumen yang valid.

Jadi, pertama dan terpenting Anda harus memutuskan apakah nullmerupakan nilai yang diperbolehkan untuk argumen Anda tentang metode itu. Jika ya, maka Anda tidak perlu pengecualian. Jika tidak, maka Anda perlu pengecualian.

Apakah Anda ingin mengizinkan null, atau tidak, masih bisa diperdebatkan, seperti yang ditunjukkan oleh banyak dan banyak hit Google tentang itu. Artinya, Anda tidak akan mendapatkan jawaban yang jelas, dan itu tergantung pada pendapat dan tradisi di tempat Anda bekerja.

Perselisihan lain adalah apakah fungsi perpustakaan harus benar - benar ketat tentang hal-hal seperti itu, atau serendah mungkin, selama itu tidak berusaha untuk "memperbaiki" parameter yang salah. Bandingkan ini dengan dunia protokol jaringan, transfer surat dll. (Yang merupakan kontrak antarmuka, seperti metode dalam pemrograman). Di sana, biasanya, kebijakannya adalah bahwa pengirim harus sesesuaian mungkin dengan protokol, sementara penerima harus berusaha agar dapat bekerja dengan apa pun yang datang.

Jadi, Anda harus memutuskan: Apakah ini benar-benar tugas metode perpustakaan untuk menegakkan kebijakan yang agak besar seperti nullpenanganan? Terutama jika perpustakaan Anda digunakan oleh orang lain juga (yang mungkin memiliki kebijakan berbeda).

Saya mungkin akan berbuat salah di sisi memberikan nullnilai, mendefinisikan dan mendokumentasikan semantik mereka (yaitu, null = empty arraydalam hal ini), dan tidak melempar pengecualian, kecuali 99% dari kode lain yang serupa (kode gaya "perpustakaan") melakukannya dengan cara lain 'bulat.

AnoE
sumber
1
"Apakah ini benar-benar tugas metode perpustakaan untuk menegakkan kebijakan yang agak besar seperti penanganan nol?" - ke bawah garis pemikiran kebohongan menegaskan dan mode debug, yang memungkinkan Anda untuk membantu menegakkan kebijakan, daripada benar-benar menegakkannya, jika itu yang ingin Anda lakukan. Jadi, jika Anda akan menelan nullprinsip menjadi liberal dalam apa yang Anda terima, maka penebangan atau bahkan melempar akan membantu orang-orang yang niatnya harus ketat dalam apa yang mereka lewati, tetapi yang telah mengacaukan kode dan unit mereka menguji sejauh yang mereka lewati null.
Steve Jessop
@ SveveJessop, saya tidak benar-benar tahu apakah Anda berdebat dengan semangat jawaban saya, atau apakah Anda melanjutkannya. Yang mengatakan, saya tidak menyarankan untuk hanya menelan null, tetapi untuk menyatakannya secara terbuka dalam kontrak metode yang dapat diterima (atau tidak, tergantung pada solusi apa pun yang muncul OP), dan kemudian pertanyaan apakah akan melemparkan pengecualian (atau tidak) memecahkan sendiri.
AnoE