Apakah jaminan kekekalan merupakan justifikasi untuk mengekspos suatu bidang alih-alih sebuah properti?

10

Panduan umum untuk C # adalah selalu menggunakan properti di bidang publik. Ini masuk akal - dengan mengekspos bidang, Anda mengekspos banyak detail implementasi. Dengan properti, Anda merangkum detail itu sehingga tersembunyi dari penggunaan kode, dan perubahan implementasi dipisahkan dari perubahan antarmuka.

Namun, saya bertanya-tanya apakah terkadang ada pengecualian yang valid untuk aturan ini ketika berhadapan dengan readonlykata kunci. Dengan menerapkan kata kunci ini ke bidang publik, Anda membuat jaminan ekstra: imutabilitas. Ini bukan hanya detail implementasi, imutabilitas adalah sesuatu yang mungkin diminati konsumen. Menggunakan readonlybidang menjadikannya bagian dari kontrak publik, dan sesuatu yang tidak dapat dipatahkan oleh perubahan atau pewarisan masa depan tanpa harus memodifikasi antarmuka publik. Itu sesuatu yang tidak bisa ditawarkan oleh properti.

Jadi, apakah jaminan ketidakmampuan merupakan alasan yang sah untuk memilih readonlybidang daripada properti dalam beberapa kasus?

(Untuk klarifikasi, saya tentu tidak mengatakan Anda harus selalu membuat pilihan ini hanya karena lapangan terjadi menjadi berubah saat ini, hanya ketika masuk akal sebagai bagian dari desain kelas dan itu penggunaan dimaksudkan untuk mencakup kekekalan dalam kontrak. Saya sebagian besar tertarik pada jawaban yang fokus pada apakah ini dapat dibenarkan, daripada kasus-kasus tertentu di mana tidak, seperti ketika Anda membutuhkan anggota untuk berada di interface, atau ingin melakukan pemuatan malas.)

Ben Aaronson
sumber
1
@gnat Hanya untuk memperjelas, dengan "Properti ReadOnly" mereka menggunakan terminologi VB.NET yang berarti properti tanpa setter, bukan bidang readonly. Untuk keperluan pertanyaan saya, dua opsi yang dibandingkan dengan pertanyaan itu setara - keduanya menggunakan properti.
Ben Aaronson

Jawaban:

6

Bidang baca statis publik tentu saja oke. Mereka direkomendasikan untuk situasi tertentu, seperti ketika Anda menginginkan objek yang bernama dan konstan tetapi Anda tidak dapat menggunakan constkata kunci.

Bidang anggota yang hanya baca agak sulit. Tidak ada banyak keuntungan yang diperoleh atas properti tanpa setter publik, dan ada kelemahan utama: bidang tidak bisa menjadi anggota antarmuka. Jadi, jika Anda memutuskan untuk membuat ulang kode untuk beroperasi dengan antarmuka alih-alih jenis konkret, Anda harus mengubah bidang yang hanya baca menjadi properti dengan getter publik.

Properti non-virtual publik tanpa setter yang dilindungi memberikan perlindungan terhadap warisan, kecuali kelas yang diwarisi memiliki akses ke bidang dukungan. Anda dapat sepenuhnya menegakkan kontrak itu di kelas dasar.

Tidak bisa mengubah "keabadian" anggota tanpa mengubah antarmuka publik kelas tidak banyak penghalang dalam kasus ini. Biasanya jika Anda ingin mengubah bidang publik menjadi properti, itu berjalan lancar kecuali bahwa ada dua kasus yang harus Anda tangani:

  1. Apa pun yang menulis ke anggota objek itu, seperti: object.Location.X = 4hanya mungkin jika Locationmerupakan bidang. Ini tidak relevan untuk bidang hanya baca, karena Anda tidak dapat mengubah struct baca saja. (Ini dengan asumsi Locationadalah tipe nilai - jika tidak maka masalah ini tidak akan berlaku karena readonlytidak melindungi terhadap hal semacam itu.)
  2. Panggilan apa pun ke metode yang meneruskan nilai sebagai outatau ref. Sekali lagi, ini tidak benar-benar relevan untuk bidang readonly, karena outdan refparameter cenderung dimodifikasi, dan ini merupakan kesalahan kompiler untuk meneruskan nilai readonly sebagai out atau ref.

Dengan kata lain, meskipun mengonversi bidang baca menjadi properti baca hanya merusak kompatibilitas biner, kode sumber lain hanya perlu dikompilasi ulang tanpa perubahan apa pun untuk menangani perubahan ini. Itu membuat jaminan sangat mudah untuk dilanggar.

Saya tidak melihat kelebihan pada readonlybidang anggota, dan ketidakmampuan untuk memiliki hal semacam itu di antarmuka cukup merugikan bagi saya bahwa saya tidak akan menggunakannya.

Erik
sumber
Dari bunga, mengapa adalah public static readonly bidang oke? Apakah itu hanya karena mengesampingkan metode instance menghapus sebagian besar kasus penggunaan untuk properti di bidang yang tidak dapat diubah (seperti penghitungan hit, pemuatan malas, dll.)?
Ben Aaronson
Kerugian utama dari bidang readonly publik vs properti readonly hilang - anggota statis tidak dapat menjadi bagian dari antarmuka sehingga mereka berada pada pijakan yang sama. Properti statis yang bisa dibaca baik membutuhkan penyimpanan yang diinisialisasi atau perlu membangun kembali nilai pengembaliannya setiap kali dipanggil, sedangkan bidang yang hanya dibaca akan memiliki sintaksis yang lebih sederhana dan diinisialisasi oleh konstruktor statis. Jadi saya pikir bidang baca statis publik memiliki potensi untuk optimasi yang lebih baik oleh JIT dengan tidak ada kerugian yang biasanya Anda miliki dari mengekspos bidang.
Erik
2

Dari pengalaman saya, readonlykata kunci bukanlah keajaiban yang dijanjikannya.

Misalnya, Anda memiliki kelas di mana konstruktor dulu sederhana. Mengingat penggunaannya, (dan fakta bahwa beberapa properti / bidang kelas tidak dapat diubah setelah konstruksi) Anda mungkin berpikir bahwa itu tidak masalah, jadi Anda menggunakan readonlykata kunci.

Kemudian, kelas menjadi lebih kompleks, dan begitu pula konstruktornya. (Katakanlah ini terjadi dalam proyek yang bersifat eksperimental atau memiliki kecepatan cukup tinggi zoom di luar ruang lingkup / kontrol tetapi Anda perlu membuatnya bekerja entah bagaimana.) Anda akan menemukan bahwa bidang yang readonlyhanya dapat dimodifikasi di dalam konstruktor - Anda tidak dapat memodifikasinya dalam metode bahkan jika metode itu hanya dipanggil dari konstruktor.

Keterbatasan teknis ini telah diakui oleh perancang C #, dan mungkin diperbaiki di versi mendatang. Tetapi sampai diperbaiki, penggunaan readonlylebih terbatas dan mungkin mendorong beberapa gaya pengkodean yang buruk (meletakkan semuanya dalam metode konstruktor).

Sebagai pengingat, baik properti readonlynon-publik-setter tidak memberikan jaminan atas "objek yang diinisialisasi penuh". Dengan kata lain, perancang kelaslah yang memutuskan apa yang dimaksud "diinisialisasi penuh", dan bagaimana melindunginya terhadap penggunaan yang tidak disengaja, dan perlindungan semacam itu pada umumnya tidak aman, yang berarti bahwa seseorang mungkin menemukan jalan keluarnya.

rwong
sumber
1
Saya lebih suka untuk membuat konstruktor tetap sederhana - lihat brendan.enrick.com/post/… , blog.ploeh.dk/2011/03/03/InjectionConstructorsharus sederhana sehingga pada kenyataannya, readonly mendorong gaya pengkodean yang baik
AlexFoxGill
1
Konstruktor dapat meneruskan readonlybidang sebagai parameter outatau refke metode lain, yang kemudian akan bebas untuk memodifikasinya sesuai keinginan mereka.
supercat
1

Fields adalah SELALU detail implementasi. Anda tidak ingin mengekspos detail implementasi Anda.

Prinsip mendasar dari rekayasa perangkat lunak adalah bahwa kita tidak tahu apa persyaratannya di masa depan. Meskipun readonlybidang Anda mungkin bagus hari ini, tidak ada yang menyarankan bahwa itu akan menjadi bagian dari persyaratan besok. Bagaimana jika besok ada kebutuhan untuk menerapkan hit counter untuk menentukan berapa kali bidang itu diakses? Bagaimana jika Anda perlu mengizinkan sub-kelas untuk menimpa bidang?

Properti sangat mudah digunakan dan jauh lebih fleksibel daripada bidang publik sehingga saya tidak bisa memikirkan satu kasus penggunaan di mana saya akan memilih c # sebagai bahasa saya dan menggunakan bidang yang hanya bisa dibaca publik di kelas. Jika kinerjanya sangat ketat sehingga saya tidak bisa menerima panggilan metode tambahan, saya mungkin akan menggunakan bahasa yang berbeda.

Hanya karena kita dapat melakukan sesuatu dengan bahasa itu tidak berarti kita harus melakukan sesuatu dengan bahasa itu.

Saya benar-benar bertanya-tanya apakah para perancang bahasa menyesal membiarkan bidang-bidang diumumkan kepada publik. Saya tidak ingat kapan terakhir kali saya mempertimbangkan untuk menggunakan bidang publik non-const dalam kode saya.

Stephen
sumber
1
Saya mengerti pentingnya membangun fleksibilitas di masa depan, tetapi tentu saja jika saya menulis kelas seperti, katakanlah, Rationaldengan publik, tidak berubah Numeratordan Denominatoranggota, saya bisa sangat yakin bahwa anggota tidak akan memerlukan pemuatan malas atau hit counter atau serupa?
Ben Aaronson
Tetapi dapatkah Anda yakin bahwa implementasi menggunakan floattipe untuk Numeratordan Denominatortidak perlu diubah doubles?
Stephen
1
Nah, eh, tidak, mereka pasti harus tidak floatatau double. Mereka seharusnya int, longatau BigInteger. Mungkin itu perlu diubah ... tetapi jika demikian mereka harus berubah di antarmuka publik juga, jadi benar-benar menggunakan properti tidak menambahkan enkapsulasi di sekitar detail itu.
Ben Aaronson
-3

Tidak. Itu bukan pembenaran.

Menggunakan readonly sebagai jaminan ketidakmungkinan untuk tidak dibenarkan lebih seperti peretasan.

Readonly berarti nilai diberikan oleh konstruktor o ketika variabel didefinisikan.

Saya pikir ini akan menjadi praktik yang buruk

Jika Anda benar-benar ingin menjamin kekekalan menggunakan antarmuka itu memiliki lebih banyak pro daripada kontra.

Contoh antarmuka sederhana: masukkan deskripsi gambar di sini

Pablo Granados
sumber
1
"gunakan antarmuka" Bagaimana?
Ben Aaronson