Dengan sengaja meningkatkan pengecualian untuk menggunakan tangkapan

10

Untuk tipikal yang if...elsedibungkus dengan penanganan pengecualian, apakah contoh berikut ini merupakan praktik yang disarankan untuk menghindari duplikasi kode?

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        throw new Exception();
    }
catch(Exception ex)
{
    return null;
}

dari pada...

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        return null;
    }
}
catch(Exception ex)
{
    return null;
}

Saya tahu ada sedikit peningkatan kinerja, tapi saya bertanya-tanya apakah ini dianggap sebagai praktik yang dapat diterima. Saat ini saya melakukannya dengan metode kedua - terutama dalam kasus di mana saya perlu menangani pengecualian khusus secara berbeda - tetapi saya bertanya-tanya apakah metode pertama sesuai untuk kasus-kasus sederhana.

grovesNL
sumber
jika metode ini cukup kecil saya hanya akan menghapus yang lain dan mengembalikan nol di luar blok trycatch jadi saya harus mengembalikan nol hanya sekali.
Fabio Marcolini

Jawaban:

12

Menggunakan penanganan pengecualian untuk kontrol aliran tidak disarankan oleh Microsoft.

Dan meja bundar tentang topik tersedia.

Yang sedang berkata, C # mendukung melakukannya, dan saya kira itu tergantung pada kondisi yang dihadapi apakah pengecualian adalah respon yang paling tepat.

B2K
sumber
1
Itu mengejutkan saya bahwa hal semacam ini hanya berusaha sangat keras untuk tidak menggunakan acara.
radarbob
@radarbob: Bagaimana acara terkait dengan ini?
@ grovesNL - Melontarkan pengecualian pada titik tertentu untuk memanggil metode tertentu di blok tangkap? Dukun seperti acara bagi saya.
radarbob
@radarbob: Ini bukan acara. Ada banyak contoh kasus penggunaan di mana ini akan digunakan, seperti yang dibahas dalam tautan meja bundar jawaban.
1
@radarbob Hanya sedikit klarifikasi, pengecualian dirancang sebagai cara menandakan pemanggil bahwa sesuatu telah terjadi yang metode yang dipanggil tidak dapat menangani. Namun, suatu peristiwa harus didengarkan. Pengecualian adalah gangguan paksa terhadap aliran normal suatu program. Pengecualian tanpa tertangkap akan menyebabkan seluruh aplikasi dibatalkan.
6

Hit kinerja paling mungkin diabaikan, seperti dijelaskan dalam jawaban ini .

Jadi mari kita pergi dengan gagasan bahwa kinerja bukan masalah. Anda melempar System.Exception, hanya untuk memindahkan eksekusi ke catchklausa . Melempar a BadControlFlowThatShouldBeRewrittenExceptionmungkin akan berlebihan.

Mari kita hancurkan ini. Kita punya:

  • Metode GetDataFromServer(nama metode harus PascalCase dalam C #), yang mungkin dapat melempar pengecualian, atau mengembalikan a bool.
  • Jika hasilnya true, jalankan ProcessData.
  • Kembali nullsebaliknya.

Sepertinya metode di mana kode ini ditulis, hanya melakukan terlalu banyak hal. GetDataFromServermengembalikan booltampilan seperti cacat desain, saya akan mengharapkan metode untuk mengembalikan data yang didapatnya dari server , beberapa IEnumerable<SomeType>yang akan berisi 0 atau lebih item - yaitu jalur bahagia mengembalikan n item di mana n> 0 , tidak begitu bahagia path mengembalikan 0 item, dan path yang tidak bahagia meledak dengan pengecualian yang tidak tertangani, apa pun itu.

Itu mengubah seperti apa metodenya, cukup banyak - sekali lagi sulit untuk mengatakan apakah ini masuk akal, karena posting asli hanya memiliki satu titik keluar (dan dengan demikian tidak dapat dikompilasi, karena tidak semua jalur kode mengembalikan nilai ), jadi ini hanya tebakan liar:

try
{
    var result = GetDataFromServer();
    return ProcessData(result);
}
catch
{
    return null;
}

Di sini Anda akan melihat ProcessDatadan melihat bahwa iterasi result, dan kembali nulljika tidak ada item di IEnumerable.

Sekarang mengapa metodenya kembali null? Server sedang down? Apakah ada bug dalam kueri? String koneksi menggunakan kredensial yang salah? Setiap kali GetDataFromServermeledak dengan pengecualian yang tidak Anda harapkan, Anda menelannya, mendorongnya di bawah karpet dan mengembalikan nullnilai. Saya akan merekomendasikan menangkap pengecualian khusus dalam kasus ini, dan mencatat semuanya; debugging akan jauh lebih mudah seperti itu.

Dengan catchklausa umum yang tidak menangkap pengecualian, akan sangat sulit untuk mendiagnosis apa pun. Saya akan melakukan ini secara minimal:

catch(Exception e)
{
    return null;
}

Sekarang Anda setidaknya bisa istirahat dan memeriksa ejika ada masalah.


TL; DR : Tidak, melempar dan menangkap pengecualian untuk kontrol aliran bukanlah ide yang baik.

Mathieu Guindon
sumber
Jawaban ini menunjukkan persis mengapa saya mencoba untuk menjaga kode saya generik: Saya tidak ingin mencantumkan setiap pengecualian yang sebenarnya saya buat dalam kode saya; Saya tidak ingin mendaftar nama metode yang sebenarnya; Saya tidak ingin mendaftar deklarasi metode; Saya tidak ingin saran sintaks. Saya punya satu pertanyaan tentang apakah melempar pengecualian untuk kontrol aliran tidak apa-apa, yang segera dijawab oleh B2K. Saya akan senang memperdebatkan ini pada meta.
3
Kedengarannya seperti ini seharusnya menjadi pertanyaan untuk Programmer saat itu. kami meninjau kode, bukan ide.
Maleakhi
2

dalam jawaban pertama Anda ada hit kinerja yang tidak perlu ada di sana.

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        throw new Exception();
    }
catch(Exception ex)
{
    return null;
}

ketika Anda keluar dari pernyataan if untuk masuk ke pernyataan Catch ketika Anda tidak harus membuat arah pengalihan kode, bisa dikatakan.

jika Anda ingin return null; melakukannya dalam pernyataan lain tidak dalam tangkapan yang ditangkap setelah dilemparkan dari pernyataan lain.

Mungkin tidak berlaku untuk Anda Estat kode, tapi untuk kode Generik yang Anda berikan itu tidak berlaku.

Standar mengatakan Anda tidak boleh melakukan ini.

Standar mengatakan Anda harus melakukannya seperti ini, (sekali lagi berdasarkan pada Kode Generik yang Diberikan dalam OP)

if (GetDataFromServer())
{
    return ProcessData();
}
else
{
    Return null
}

dan karena Anda tidak memiliki pengecualian khusus yang Anda tangkap, Anda bahkan tidak boleh mencoba menangkapnya di sini.

Anda ingin melihat Pengecualian saat itu terjadi sehingga Anda dapat memperbaiki masalah yang menciptakan pengecualian.

Maleakhi
sumber
1

Kenapa tidak jauh lebih sederhana:

if (!GetDataFromServer()) return null;
ProcessData();

Jika eksepsi handler akan ada, seharusnya di ProcessData ()

Loren Pechtel
sumber
Mengapa saya tidak ingin meneruskan pengecualian ProcessData()ke tingkat paling atas?
grovesNL
@ grovesNL Tidak ada yang berguna dilakukan dengan pengecualian di sini.
Loren Pechtel
1
Bagaimana? Jika ProcessData()melempar pengecualian sekarang tidak ditangani. Saya ingin return nulldi level ini jika ProcessData()melempar pengecualian, tanpa memodifikasi ProcessData()sendiri.
grovesNL