Mengapa kode C # berikut tidak diizinkan:
public abstract class BaseClass
{
public abstract int Bar { get;}
}
public class ConcreteClass : BaseClass
{
public override int Bar
{
get { return 0; }
set {}
}
}
CS0546 'ConcreteClass.Bar.set': tidak dapat menimpa karena 'BaseClass.Bar' tidak memiliki set accessor yang dapat ditimpa
c#
.net
properties
getter-setter
ripper234
sumber
sumber
Foo
yang tidak melakukan apa pun selain membungkusGetFoo
metode abstrak yang dilindungi . Tipe turunan abstrak dapat membayangi properti dengan properti baca-tulis non-virtual yang tidak melakukan apa pun selain membungkusGetFoo
metode tersebut bersama denganSetFoo
metode abstrak ; jenis turunan beton dapat melakukan hal di atas tetapi memberikan definisi untukGetFoo
danSetFoo
.Jawaban:
Karena penulis Baseclass secara eksplisit menyatakan bahwa Bar harus menjadi properti hanya-baca. Tidak masuk akal jika derivasi melanggar kontrak ini dan membuatnya baca-tulis.
Saya dengan Microsoft yang satu ini.
Katakanlah saya seorang programmer baru yang telah diberi tahu kode terhadap derivasi Baseclass. saya menulis sesuatu yang mengasumsikan bahwa Bar tidak dapat dituliskan (karena Baseclass secara eksplisit menyatakan bahwa itu adalah properti get only). Sekarang dengan derivasi Anda, kode saya mungkin rusak. misalnya
Karena Bar tidak dapat diatur sesuai antarmuka BaseClass, BarProvider mengasumsikan bahwa caching adalah hal yang aman untuk dilakukan - Karena Bar tidak dapat dimodifikasi. Tetapi jika set dimungkinkan dalam derivasi, kelas ini bisa menyajikan nilai basi jika seseorang memodifikasi properti Bar objek _source secara eksternal. Intinya adalah ' Terbuka, hindari melakukan hal-hal licik dan mengejutkan orang '
Pembaruan : Ilya Ryzhenkov bertanya 'Mengapa antarmuka tidak bermain dengan aturan yang sama?' Hmm .. ini jadi lebih kacau saat kupikirkan.
Antarmuka adalah kontrak yang mengatakan 'mengharapkan implementasi memiliki properti baca bernama Bar.' Secara pribadi saya jauh lebih kecil kemungkinannya untuk membuat asumsi read-only jika saya melihat sebuah Interface. Ketika saya melihat properti get-only pada antarmuka, saya membacanya sebagai 'Implementasi apa pun akan mengekspos bar atribut ini' ... pada kelas dasar itu klik sebagai 'Bar adalah properti read-only'. Tentu saja secara teknis Anda tidak melanggar kontrak .. Anda melakukan lebih banyak. Jadi Anda benar dalam arti tertentu .. Saya akan menutup dengan mengatakan 'buat sesulit mungkin untuk kesalahpahaman muncul'.
sumber
InvalidOperationException("This instance is read-only")
.Saya pikir alasan utamanya adalah karena sintaksinya terlalu eksplisit untuk dapat bekerja dengan cara lain. Kode ini:
cukup eksplisit bahwa keduanya
get
danset
yang ditimpa. Tidak adaset
di kelas dasar, jadi kompiler mengeluh. Sama seperti Anda tidak bisa mengganti metode yang tidak didefinisikan di kelas dasar, Anda juga tidak bisa menimpa setter.Anda mungkin mengatakan bahwa kompiler harus menebak niat Anda dan hanya menerapkan override ke metode yang dapat diganti (yaitu pengambil dalam kasus ini), tetapi ini bertentangan dengan salah satu prinsip desain C # - bahwa kompiler tidak boleh menebak niat Anda , karena mungkin salah menebak tanpa Anda sadari.
Saya pikir sintaks berikut mungkin bekerja dengan baik, tetapi seperti Eric Lippert terus katakan, mengimplementasikan bahkan fitur kecil seperti ini masih merupakan upaya besar ...
atau, untuk properti yang diimplementasikan secara otomatis,
sumber
Saya menemukan masalah yang sama hari ini dan saya pikir memiliki alasan yang sangat valid untuk menginginkan ini.
Pertama saya ingin berdebat bahwa memiliki properti get-only tidak harus diterjemahkan menjadi read-only. Saya menafsirkannya sebagai "Dari antarmuka ini / abtract Anda bisa mendapatkan nilai ini", itu tidak berarti bahwa beberapa implementasi dari antarmuka / kelas abstrak tidak memerlukan pengguna / program untuk mengatur nilai ini secara eksplisit. Kelas abstrak melayani tujuan penerapan bagian dari fungsionalitas yang diperlukan. Saya sama sekali tidak melihat alasan mengapa kelas yang diwariskan tidak dapat menambahkan setter tanpa melanggar kontrak apa pun.
Berikut ini adalah contoh sederhana dari apa yang saya butuhkan hari ini. Saya akhirnya harus menambahkan setter di antarmuka saya hanya untuk menyiasati ini. Alasan untuk menambahkan setter dan tidak menambahkan, katakanlah, metode SetProp adalah bahwa salah satu implementasi khusus antarmuka menggunakan DataContract / DataMember untuk serialisasi Prop, yang akan dibuat rumit tanpa perlu jika saya harus menambahkan properti lain hanya untuk tujuan tersebut serialisasi.
Saya tahu ini hanya posting "2 sen" saya, tetapi saya merasa dengan poster asli dan mencoba merasionalisasi bahwa ini adalah hal yang baik tampaknya aneh bagi saya, terutama mengingat keterbatasan yang sama tidak berlaku ketika mewarisi langsung dari sebuah warisan antarmuka.
Juga menyebutkan tentang menggunakan baru alih-alih menimpa tidak berlaku di sini, itu hanya tidak berfungsi dan bahkan jika itu tidak akan memberi Anda hasil yang diinginkan, yaitu pengambil virtual seperti yang dijelaskan oleh antarmuka.
sumber
Itu mungkin
tl; dr - Anda dapat mengganti metode get-only dengan setter jika Anda mau. Ini pada dasarnya hanya:
Buat
new
properti yang memiliki aget
dan aset
menggunakan nama yang sama.Jika Anda tidak melakukan hal lain, maka
get
metode lama akan tetap dipanggil ketika kelas turunan dipanggil melalui tipe dasarnya. Untuk memperbaikinya, tambahkanabstract
lapisan perantara yang menggunakan metodeoverride
lamaget
untuk memaksanya mengembalikan hasilget
metode baru .Ini memungkinkan kami untuk menimpa properti dengan
get
/set
bahkan jika mereka tidak memiliki satu dalam definisi dasar mereka.Sebagai bonus, Anda juga dapat mengubah jenis pengembalian jika diinginkan.
Jika definisi dasar adalah
get
-hanya, maka Anda dapat menggunakan tipe pengembalian yang lebih diturunkan.Jika definisi dasar adalah
set
-hanya, maka Anda dapat menggunakan tipe pengembalian yang kurang diturunkan.Jika definisi dasar sudah
get
/set
, maka:Anda dapat menggunakan tipe pengembalian yang lebih diturunkan jika Anda membuatnya
set
-hanya;Anda bisa menggunakan tipe pengembalian yang kurang diturunkan jika Anda membuatnya
get
-hanya.Dalam semua kasus, Anda dapat menyimpan tipe pengembalian yang sama jika Anda mau. Contoh di bawah ini menggunakan tipe pengembalian yang sama untuk kesederhanaan.
Situasi:
get
Properti -hanya ada sebelumnyaAnda memiliki beberapa struktur kelas yang tidak dapat Anda modifikasi. Mungkin hanya satu kelas, atau itu pohon warisan yang sudah ada sebelumnya. Apa pun masalahnya, Anda ingin menambahkan
set
metode ke properti, tetapi tidak bisa.Masalah: Tidak bisa
override
denganget
-hanya denganget
/set
Anda ingin
override
denganget
/set
properti, tetapi tidak dapat dikompilasi.Solusi: Gunakan
abstract
lapisan perantaraMeskipun Anda tidak dapat langsung
override
denganget
/set
properti, Anda dapat :Buat
new
get
/set
properti dengan nama yang sama.override
get
metode lama dengan accessor keget
metode baru untuk memastikan konsistensi.Jadi, pertama Anda menulis
abstract
lapisan perantara:Kemudian, Anda menulis kelas yang tidak akan dikompilasi sebelumnya. Ini akan mengkompilasi kali ini karena Anda tidak benar
override
- benar menggunakanget
properti -hanya; alih-alih, Anda menggantinya menggunakannew
kata kunci.Hasil: Semuanya berfungsi!
Jelas, ini berfungsi seperti yang dimaksudkan untuk
D
.Semuanya masih berfungsi sebagaimana dimaksudkan saat melihat
D
sebagai salah satu kelas dasar, misalnyaA
atauB
. Tapi, alasan mengapa itu bekerja mungkin sedikit kurang jelas.Namun, definisi kelas dasar
get
masih pada akhirnya ditimpa oleh definisi kelas turunanget
, sehingga masih sepenuhnya konsisten.Diskusi
Metode ini memungkinkan Anda untuk menambahkan
set
metode keget
properti -hanya. Anda juga dapat menggunakannya untuk melakukan hal-hal seperti:Ubah properti apa pun menjadi properti
get
-only,set
-only, atauget
-and-set
, terlepas dari apa itu di kelas dasar.Ubah tipe pengembalian metode dalam kelas turunan.
Kelemahan utama adalah bahwa ada lebih banyak pengkodean untuk dilakukan dan tambahan
abstract class
di pohon warisan. Ini bisa sedikit menjengkelkan dengan konstruktor yang mengambil parameter karena harus disalin / ditempelkan di lapisan perantara.sumber
Saya setuju bahwa tidak bisa mengganti pengambil dalam tipe turunan adalah anti-pola. Read-Only menentukan kurangnya implementasi, bukan kontrak fungsional murni (tersirat oleh jawaban suara teratas).
Saya menduga Microsoft memiliki batasan ini baik karena kesalahpahaman yang sama dipromosikan, atau mungkin karena menyederhanakan tata bahasa; meskipun, sekarang ruang lingkup itu dapat diterapkan untuk mendapatkan atau mengatur secara individual, mungkin kita bisa berharap menimpanya juga.
Kesalahpahaman yang ditunjukkan oleh jawaban suara terbanyak, bahwa properti baca-saja entah bagaimana harus lebih "murni" daripada properti baca / tulis itu konyol. Cukup lihat banyak properti read only yang umum dalam framework; nilainya tidak konstan / murni fungsional; misalnya, DateTime. Sekarang hanya bisa dibaca, tetapi apa pun kecuali nilai fungsional murni. Upaya 'cache' nilai properti hanya baca dengan asumsi akan mengembalikan nilai yang sama lain kali berisiko.
Bagaimanapun, saya telah menggunakan salah satu strategi berikut untuk mengatasi keterbatasan ini; keduanya kurang sempurna, tetapi akan memungkinkan Anda pincang melampaui kekurangan bahasa ini:
sumber
Anda mungkin dapat mengatasi masalah dengan membuat properti baru:
sumber
Saya dapat memahami semua poin Anda, tetapi secara efektif, properti otomatis C # 3.0 menjadi tidak berguna dalam hal ini.
Anda tidak dapat melakukan hal seperti itu:
IMO, C # seharusnya tidak membatasi skenario seperti itu. Merupakan tanggung jawab pengembang untuk menggunakannya sesuai kebutuhan.
sumber
Masalahnya adalah bahwa untuk alasan apa pun Microsoft memutuskan bahwa harus ada tiga jenis properti: baca-saja, tulis-saja, dan baca-tulis, hanya satu yang mungkin ada dengan tanda tangan yang diberikan dalam konteks tertentu; properti hanya dapat diganti oleh properti yang dideklarasikan secara identik. Untuk melakukan apa yang Anda inginkan, Anda perlu membuat dua properti dengan nama dan tanda tangan yang sama - satu di antaranya hanya baca, dan satu di antaranya adalah baca-tulis.
Secara pribadi, saya berharap bahwa seluruh konsep "properti" dapat dihapuskan, kecuali bahwa sintaksis properti-ish dapat digunakan sebagai gula sintaksis untuk memanggil metode "get" dan "set". Ini tidak hanya memfasilitasi opsi 'add set', tetapi juga akan memungkinkan 'get' untuk mengembalikan tipe yang berbeda dari 'set'. Sementara kemampuan seperti itu tidak akan sering digunakan, kadang-kadang bisa berguna untuk memiliki metode 'dapatkan' mengembalikan objek pembungkus sementara 'set' dapat menerima pembungkus atau data aktual.
sumber
Berikut ini solusi untuk mencapai hal ini menggunakan Refleksi:
Dengan cara ini kita dapat menetapkan nilai objek pada properti yang hanya bisa dibaca. Mungkin tidak akan berfungsi di semua skenario!
sumber
Anda harus mengubah judul pertanyaan Anda menjadi detail bahwa pertanyaan Anda semata-mata berkaitan dengan mengesampingkan properti abstrak, atau bahwa pertanyaan Anda adalah sehubungan dengan umumnya mengesampingkan properti hanya-mendapatkan kelas.
Jika yang pertama (mengesampingkan properti abstrak)
Kode itu tidak berguna. Kelas dasar saja seharusnya tidak memberi tahu Anda bahwa Anda dipaksa untuk menimpa properti Get-Only (Mungkin Antarmuka). Kelas dasar menyediakan fungsionalitas umum yang mungkin memerlukan input spesifik dari kelas pelaksana. Oleh karena itu, fungsi umum dapat membuat panggilan ke properti atau metode abstrak. Dalam kasus yang diberikan, metode fungsionalitas umum harus meminta Anda untuk mengganti metode abstrak seperti:
Tetapi jika Anda tidak memiliki kendali atas hal itu, dan fungsionalitas kelas dasar membaca dari milik publiknya sendiri (aneh), maka lakukan saja ini:
Saya ingin menunjukkan komentar (aneh): Saya akan mengatakan praktik terbaik adalah untuk kelas tidak menggunakan properti publiknya sendiri, tetapi untuk menggunakan bidang privat / dilindungi ketika ada. Jadi ini adalah pola yang lebih baik:
Jika yang terakhir (menimpa properti get-only kelas)
Setiap properti non-abstrak memiliki setter. Kalau tidak, itu tidak berguna dan Anda tidak perlu peduli untuk menggunakannya. Microsoft tidak harus mengizinkan Anda untuk melakukan apa yang Anda inginkan. Alasannya: setter ada dalam beberapa bentuk atau lainnya, dan Anda dapat mencapai apa yang Anda inginkan dengan mudah kepada Veerryy .
Kelas dasar, atau kelas mana pun tempat Anda dapat membaca properti
{get;}
, memiliki BEBERAPA jenis penyetel terbuka untuk properti itu. Metadata akan terlihat seperti ini:Tetapi implementasinya akan memiliki dua ujung spektrum kompleksitas:
Kompleks Paling Sedikit:
Paling Kompleks:
Maksud saya, bagaimanapun, Anda memiliki setter terbuka. Dalam kasus Anda, Anda mungkin ingin menimpa
int Bar
karena Anda tidak ingin kelas dasar menanganinya, tidak memiliki akses untuk meninjau bagaimana penanganannya, atau ditugaskan untuk membajak beberapa kode nyata dengan cepat dan tidak bertentangan dengan keinginan Anda .Dalam Latter dan Former (Kesimpulan)
Cerpen Panjang: Tidak perlu bagi Microsoft untuk mengubah apa pun. Anda dapat memilih bagaimana kelas implementasi Anda diatur dan, tanpa konstruktor, gunakan semua atau tidak ada kelas dasar.
sumber
Solusi hanya untuk sebagian kecil kasus penggunaan, namun demikian: di C # 6.0 setter "readonly" secara otomatis ditambahkan untuk properti pengambil-override saja.
sumber
Karena itu akan mematahkan konsep enkapsulasi dan implementasi persembunyian. Pertimbangkan kasus ketika Anda membuat kelas, kirimkan, dan kemudian konsumen kelas Anda membuat dirinya dapat mengatur properti yang awalnya Anda sediakan hanya untuk pengambil. Ini akan secara efektif mengganggu setiap invarian dari kelas Anda yang dapat Anda andalkan dalam implementasi Anda.
sumber
Ini bukan tidak mungkin. Anda cukup menggunakan kata kunci "baru" di properti Anda. Sebagai contoh,
Tampaknya pendekatan ini memuaskan kedua belah pihak dari diskusi ini. Menggunakan "baru" mematahkan kontrak antara implementasi kelas dasar dan implementasi subkelas. Ini diperlukan ketika suatu Kelas dapat memiliki beberapa kontrak (baik melalui antarmuka atau kelas dasar).
Semoga ini membantu
sumber
new
tidak lagi mengesampingkan basis virtual. Secara khusus, jika properti dasar adalah abstrak , seperti di posting asli, tidak mungkin untuk menggunakannew
kata kunci dalam subkelas non-abstrak karena Anda harus mengganti semua anggota abstrak.Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty)
memberi Anda 5,Console.WriteLine(b.BaseProperty)
memberi Anda 0, dan ketika penerus Anda harus men-debug ini, Anda sebaiknya pindah jauh, jauh.Properti read-only di kelas dasar menunjukkan bahwa properti ini mewakili nilai yang selalu dapat ditentukan dari dalam kelas (misalnya nilai enum yang cocok dengan konteks (db-) objek). Jadi, tanggung jawab menentukan nilai tetap berada di dalam kelas.
Menambahkan setter akan menyebabkan masalah canggung di sini: Kesalahan validasi harus terjadi jika Anda menetapkan nilai ke hal lain selain dari nilai tunggal yang mungkin sudah dimiliki.
Namun, aturan sering kali memiliki pengecualian. Sangat mungkin bahwa misalnya dalam satu kelas turunan konteks mempersempit nilai enum yang mungkin turun menjadi 3 dari 10, namun pengguna objek ini masih perlu memutuskan mana yang benar. Kelas turunan perlu mendelegasikan tanggung jawab menentukan nilai kepada pengguna objek ini. Penting untuk disadari adalah bahwa pengguna objek ini harus menyadari pengecualian ini dan memikul tanggung jawab untuk menetapkan nilai yang benar.
Solusi saya dalam situasi seperti ini adalah meninggalkan properti read-only dan menambahkan properti read-write baru ke kelas turunan untuk mendukung pengecualian. Override dari properti asli hanya akan mengembalikan nilai properti baru. Properti baru dapat memiliki nama yang tepat yang menunjukkan konteks pengecualian ini dengan benar.
Ini juga mendukung pernyataan yang sah: "buat sesulit mungkin untuk kesalahpahaman muncul" oleh Gishu.
sumber
ReadableMatrix
kelas (dengan properti terindeks read-only dua argumen) yang memiliki subtipeImmutableMatrix
danMutableMatrix
. Kode yang hanya perlu membaca matriks, dan tidak peduli apakah itu mungkin berubah beberapa waktu di masa depan, dapat menerima parameter jenisReadableMatrix
dan sangat senang dengan subtipe yang bisa berubah atau tidak berubah.List<T>.Count
dilakukan: Ini tidak dapat ditetapkan, tetapi itu tidak berarti "hanya-baca" karena tidak dapat dimodifikasi oleh pengguna kelas. Itu dapat dimodifikasi dengan sangat baik, meskipun secara tidak langsung, dengan menambahkan dan menghapus item daftar.Karena pada tingkat IL, properti baca / tulis diterjemahkan menjadi dua metode (pengambil dan penyetel).
Saat mengganti, Anda harus tetap mendukung antarmuka yang mendasarinya. Jika Anda bisa menambahkan setter, Anda akan secara efektif menambahkan metode baru, yang akan tetap tidak terlihat oleh dunia luar, sejauh menyangkut antarmuka kelas Anda.
Benar, menambahkan metode baru tidak akan melanggar kompatibilitas per se, tetapi karena itu akan tetap tersembunyi, keputusan untuk melarang ini masuk akal.
sumber
new
bukanoverride
.new
dengan tambahan Anda.Karena kelas yang memiliki properti baca-saja (tanpa setter) mungkin memiliki alasan yang bagus untuk itu. Mungkin tidak ada datastore yang mendasarinya, misalnya. Mengizinkan Anda membuat setter mematahkan kontrak yang ditetapkan oleh kelas. Itu hanya OOP yang buruk.
sumber