Kapan tepat untuk melempar pengecualian dari dalam pengambil atau penyetel properti? Kapan tidak sesuai? Mengapa? Tautan ke dokumen eksternal tentang masalah ini akan sangat membantu ... Ternyata Google sangat sedikit.
Kapan tepat untuk melempar pengecualian dari dalam pengambil atau penyetel properti? Kapan tidak sesuai? Mengapa? Tautan ke dokumen eksternal tentang masalah ini akan sangat membantu ... Ternyata Google sangat sedikit.
Jawaban:
Microsoft memiliki rekomendasinya tentang cara merancang properti di http://msdn.microsoft.com/en-us/library/ms229006.aspx
Pada dasarnya, mereka merekomendasikan agar pengambil properti menjadi pengakses ringan yang selalu aman untuk dipanggil. Mereka merekomendasikan mendesain ulang getter menjadi metode jika pengecualian adalah sesuatu yang perlu Anda buang. Untuk penyetel, mereka menunjukkan bahwa pengecualian adalah strategi penanganan kesalahan yang sesuai dan dapat diterima.
Untuk pengindeks, Microsoft menunjukkan bahwa getter dan setter boleh melempar pengecualian. Dan faktanya, banyak pengindeks di pustaka .NET melakukan ini. Pengecualian yang paling umum ArgumentOutOfRangeException
.
Ada beberapa alasan yang cukup bagus mengapa Anda tidak ingin memberikan pengecualian pada pengambil properti:
obj.PropA.AnotherProp.YetAnother
- dengan sintaks semacam ini, menjadi masalah untuk memutuskan di mana harus memasukkan pernyataan pengecualian catch.Sebagai catatan tambahan, orang harus menyadari bahwa hanya karena properti tidak dirancang untuk memberikan pengecualian, bukan berarti tidak akan; itu bisa dengan mudah memanggil kode yang melakukannya. Bahkan tindakan sederhana untuk mengalokasikan objek baru (seperti string) dapat menghasilkan pengecualian. Anda harus selalu menulis kode Anda secara defensif dan mengharapkan pengecualian dari apa pun yang Anda panggil.
Tidak ada yang salah dengan memberikan pengecualian dari setter. Lagi pula, cara apa yang lebih baik untuk menunjukkan bahwa nilainya tidak valid untuk properti tertentu?
Untuk pengambil, umumnya tidak disukai, dan itu bisa dijelaskan dengan mudah: pengambil properti, secara umum, melaporkan status saat ini dari suatu objek; dengan demikian, satu-satunya kasus di mana masuk akal bagi getter untuk melempar adalah ketika status tidak valid. Tetapi secara umum juga dianggap sebagai ide yang baik untuk mendesain kelas Anda sedemikian rupa sehingga tidak mungkin untuk mendapatkan objek yang tidak valid pada awalnya, atau memasukkannya ke dalam keadaan tidak valid melalui cara normal (yaitu, selalu pastikan inisialisasi penuh dalam konstruktor, dan coba buat metode pengecualian-aman sehubungan dengan validitas status dan invarian kelas). Selama Anda tetap berpegang pada aturan itu, pengambil properti Anda tidak boleh masuk ke situasi di mana mereka harus melaporkan keadaan tidak valid, dan karenanya tidak pernah melempar.
Ada satu pengecualian yang saya tahu, dan itu sebenarnya yang agak utama: objek apa pun yang diimplementasikan IDisposable
. Dispose
secara khusus dimaksudkan sebagai cara untuk membawa objek ke status tidak valid, dan bahkan ada kelas pengecualian khusus ObjectDisposedException
,, untuk digunakan dalam kasus itu. Sangat normal untuk membuang ObjectDisposedException
dari anggota kelas mana pun, termasuk pengambil properti (dan tidak termasuk Dispose
dirinya sendiri), setelah objek dibuang.
IDisposable
harus dibuat tidak berguna setelah a Dispose
. Jika memanggil anggota akan membutuhkan penggunaan sumber daya yang Dispose
telah dibuat tidak tersedia (misalnya anggota akan membaca data dari aliran yang telah ditutup) anggota harus membuang ObjectDisposedException
daripada membocorkan misalnya ArgumentException
, tetapi jika seseorang memiliki formulir dengan properti yang mewakili nilai dalam bidang tertentu, akan tampak jauh lebih membantu untuk memungkinkan properti tersebut dibaca setelah pembuangan (menghasilkan nilai yang diketik terakhir) daripada membutuhkan ...
Dispose
ditangguhkan sampai semua properti tersebut telah dibaca. Dalam beberapa kasus di mana satu utas mungkin menggunakan pembacaan pemblokiran pada suatu objek sementara yang lain menutupnya, dan di mana data mungkin tiba kapan saja sebelumnya Dispose
, mungkin berguna untuk Dispose
memotong data yang masuk, tetapi memungkinkan data yang diterima sebelumnya untuk dibaca. Seseorang tidak boleh memaksakan perbedaan buatan antara Close
dan Dispose
dalam situasi di mana tidak ada yang perlu ada.
Get...
metode saja. Pengecualian di sini adalah ketika Anda harus mengimplementasikan antarmuka yang ada yang mengharuskan Anda menyediakan properti.
Ini hampir tidak pernah cocok untuk pengambil, dan kadang-kadang cocok untuk penyetel.
Sumber daya terbaik untuk pertanyaan semacam ini adalah "Panduan Desain Kerangka Kerja" oleh Cwalina dan Abrams; itu tersedia sebagai buku terikat, dan sebagian besar juga tersedia secara online.
Dari bagian 5.2: Desain Properti
HINDARI melempar pengecualian dari pengambil properti. Pengambil properti harus berupa operasi sederhana dan tidak boleh memiliki prasyarat. Jika getter bisa melontarkan pengecualian, itu mungkin harus didesain ulang menjadi sebuah metode. Perhatikan bahwa aturan ini tidak berlaku untuk pengindeks, di mana kami mengharapkan pengecualian sebagai hasil dari memvalidasi argumen.
Perhatikan bahwa pedoman ini hanya berlaku untuk pengambil properti. Tidak masalah untuk membuat pengecualian dalam penyetel properti.
ObjectDisposedException
setelah objek tersebut Dispose()
memanggil dan sesuatu yang kemudian meminta nilai properti? Sepertinya panduannya harus "hindari melempar pengecualian dari pengambil properti, kecuali jika objek telah dibuang dalam hal ini Anda harus mempertimbangkan untuk melempar ObjectDisposedExcpetion".
Salah satu pendekatan yang bagus untuk Pengecualian adalah menggunakannya untuk mendokumentasikan kode untuk Anda sendiri dan pengembang lain sebagai berikut:
Pengecualian harus untuk status program yang luar biasa. Artinya tidak masalah untuk menulisnya di mana pun Anda mau!
Salah satu alasan Anda mungkin ingin menempatkannya di getter adalah untuk mendokumentasikan API kelas - jika perangkat lunak mengeluarkan pengecualian segera setelah programmer mencoba menggunakannya dengan salah maka mereka tidak akan salah menggunakannya! Misalnya jika Anda memiliki validasi selama proses membaca data, mungkin tidak masuk akal untuk dapat melanjutkan dan mengakses hasil proses jika ada kesalahan fatal dalam data. Dalam hal ini Anda mungkin ingin mendapatkan keluaran jika ada kesalahan untuk memastikan bahwa programmer lain memeriksa kondisi ini.
Mereka adalah cara untuk mendokumentasikan asumsi dan batasan dari subsistem / metode / apapun. Dalam kasus umum, mereka tidak boleh ditangkap! Ini juga karena mereka tidak pernah dilemparkan jika sistem bekerja sama dengan cara yang diharapkan: Jika terjadi pengecualian, ini menunjukkan bahwa asumsi sepotong kode tidak terpenuhi - misalnya tidak berinteraksi dengan dunia di sekitarnya dengan cara itu awalnya dimaksudkan untuk. Jika Anda menemukan pengecualian yang ditulis untuk tujuan ini, itu mungkin berarti sistem telah memasuki keadaan yang tidak dapat diprediksi / tidak konsisten - ini pada akhirnya dapat menyebabkan crash atau kerusakan data atau serupa yang kemungkinan akan jauh lebih sulit untuk dideteksi / debug.
Pesan pengecualian adalah cara yang sangat kasar untuk melaporkan kesalahan - pesan tersebut tidak dapat dikumpulkan secara massal dan hanya benar-benar berisi string. Ini membuat mereka tidak cocok untuk melaporkan masalah dalam input data. Dalam menjalankan normal, sistem itu sendiri tidak boleh memasuki status kesalahan. Akibatnya, pesan di dalamnya harus dirancang untuk pemrogram dan bukan untuk pengguna - hal-hal yang salah dalam data masukan dapat ditemukan dan diteruskan ke pengguna dalam format yang lebih sesuai (khusus).
Pengecualian (haha!) Untuk aturan ini adalah hal-hal seperti IO di mana pengecualian tidak berada di bawah kendali Anda dan tidak dapat diperiksa terlebih dahulu.
Ini semua didokumentasikan dalam MSDN (sebagaimana ditautkan dalam jawaban lain) tetapi berikut adalah aturan umum ...
Di penyetel, jika properti Anda harus divalidasi di atas dan di luar tipe. Misalnya, properti bernama PhoneNumber mungkin harus memiliki validasi regex dan akan menampilkan kesalahan jika formatnya tidak valid.
Untuk getter, mungkin jika nilainya null, tetapi kemungkinan besar itu adalah sesuatu yang ingin Anda tangani pada kode panggilan (sesuai pedoman desain).
MSDN: Jenis Pengecualian Standar Penangkapan dan Lempar
Ini adalah pertanyaan dan jawaban yang sangat kompleks tergantung pada bagaimana objek Anda digunakan. Prinsipnya, pengambil dan penyetel properti yang "mengikat terlambat" tidak boleh menampilkan pengecualian, sementara properti dengan "pengikatan awal" eksklusif harus menampilkan pengecualian saat diperlukan. BTW, alat analisis kode Microsoft mendefinisikan penggunaan properti terlalu sempit menurut saya.
"pengikatan terlambat" berarti bahwa properti ditemukan melalui refleksi. Misalnya atribut "Serializeable" digunakan untuk membuat serial / deserialisasi objek melalui propertinya. Melempar pengecualian selama situasi seperti ini akan merusak berbagai hal dengan cara yang sangat berbahaya dan bukan cara yang baik untuk menggunakan pengecualian untuk membuat kode yang lebih kuat.
"pengikatan awal" berarti bahwa penggunaan properti diikat dalam kode oleh kompilator. Misalnya ketika beberapa kode yang Anda tulis mereferensikan pengambil properti. Dalam hal ini tidak masalah untuk memberikan pengecualian jika masuk akal.
Objek dengan atribut internal memiliki status yang ditentukan oleh nilai atribut tersebut. Properti yang mengekspresikan atribut yang peka dan peka terhadap keadaan internal objek tidak boleh digunakan untuk pengikatan terlambat. Misalnya, Anda memiliki objek yang harus dibuka, diakses, lalu ditutup. Dalam kasus ini, mengakses properti tanpa memanggil buka terlebih dahulu akan menghasilkan pengecualian. Misalkan, dalam kasus ini, kita tidak melempar eksepsi dan kita mengizinkan kode mengakses suatu nilai tanpa mengeluarkan eksepsi? Kode akan tampak senang meskipun mendapat nilai dari pengambil yang tidak masuk akal. Sekarang kita telah meletakkan kode yang memanggil pengambil dalam situasi yang buruk karena harus tahu bagaimana memeriksa nilai untuk melihat apakah itu tidak masuk akal. Ini berarti bahwa kode harus membuat asumsi tentang nilai yang didapatnya dari pengambil properti untuk memvalidasinya. Beginilah cara penulisan kode yang buruk.
Saya memiliki kode ini di mana saya tidak yakin pengecualian mana yang harus dilemparkan.
public Person
{
public string Name { get; set; }
public boolean HasPets { get; set; }
}
public void Foo(Person person)
{
if (person.Name == null) {
throw new Exception("Name of person is null.");
// I was unsure of which exception to throw here.
}
Console.WriteLine("Name is: " + person.Name);
}
Saya mencegah model memiliki properti menjadi null di tempat pertama dengan memaksanya sebagai argumen di konstruktor.
public Person
{
public Person(string name)
{
if (name == null) {
throw new ArgumentNullException(nameof(name));
}
Name = name;
}
public string Name { get; private set; }
public boolean HasPets { get; set; }
}
public void Foo(Person person)
{
Console.WriteLine("Name is: " + person.Name);
}