Saya mengembalikan referensi ke kamus di properti hanya baca saya. Bagaimana cara mencegah konsumen mengubah data saya? Jika ini adalah IList
saya hanya bisa mengembalikannya AsReadOnly
. Apakah ada hal serupa yang dapat saya lakukan dengan kamus?
Private _mydictionary As Dictionary(Of String, String)
Public ReadOnly Property MyDictionary() As Dictionary(Of String, String)
Get
Return _mydictionary
End Get
End Property
.net
dictionary
readonly
Rob Sobers
sumber
sumber
Jawaban:
Berikut ini adalah implementasi sederhana yang membungkus kamus:
sumber
.NET 4.5
.NET Framework 4.5 BCL memperkenalkan
ReadOnlyDictionary<TKey, TValue>
( sumber ).Karena .NET Framework 4.5 BCL tidak menyertakan
AsReadOnly
kamus, Anda harus menulis sendiri (jika Anda menginginkannya). Ini akan menjadi sesuatu seperti yang berikut, kesederhanaan yang mungkin menyoroti mengapa itu bukan prioritas untuk .NET 4.5..NET 4.0 dan di bawah
Sebelum .NET 4.5, tidak ada kelas .NET framework yang membungkus
Dictionary<TKey, TValue>
seperti ReadOnlyCollection membungkus Daftar . Namun, tidak sulit untuk membuatnya.Berikut ini adalah contoh - ada banyak lainnya jika Anda Google untuk ReadOnlyDictionary .
sumber
AsReadOnly()
metode seperti biasaDictionary<,>
, jadi saya ingin tahu berapa banyak orang yang akan menemukan tipe baru mereka. Thread Stack Overflow ini akan membantu.Diumumkan dalam konferensi BUILD baru-baru ini bahwa sejak .NET 4.5, antarmuka
System.Collections.Generic.IReadOnlyDictionary<TKey,TValue>
disertakan. Buktinya ada di sini (Mono) dan di sini (Microsoft);)Tidak yakin jika
ReadOnlyDictionary
disertakan juga, tetapi setidaknya dengan antarmuka itu seharusnya tidak sulit untuk membuat sekarang implementasi yang memperlihatkan antarmuka .NET generik resmi :)sumber
ReadOnlyDictionary<TKey, TValue>
(.Net 4.5) - msdn.microsoft.com/en-us/library/gg712875.aspxJangan ragu untuk menggunakan pembungkus sederhana saya. Ini TIDAK mengimplementasikan IDictionary, sehingga tidak harus membuang pengecualian saat runtime untuk metode kamus yang akan mengubah kamus. Ubah metode sama sekali tidak ada. Saya membuat antarmuka saya sendiri untuk itu yang disebut IReadOnlyDictionary.
sumber
IDictionary
kontrak. Saya pikir itu lebih benar dari perspektif OOP untukIDictionary
diwarisi dariIReadOnlyDictionary
.IDictionary
(untuk saat iniIReadOnlyDictionary
) danIMutableDictionary
(untuk saat iniIDictionary
).IsReadOnly on
IDictionary<TKey,TValue>
diwarisi dariICollection<T>
(IDictionary<TKey,TValue>
extendsICollection<T>
asICollection<KeyValuePair<TKey,TValue>>
). Itu tidak digunakan atau diimplementasikan dengan cara apa pun (dan sebenarnya "tersembunyi" melalui penggunaan secara eksplisit menerapkanICollection<T>
anggota terkait ).Setidaknya ada 3 cara yang bisa saya lihat untuk menyelesaikan masalah:
IDictionary<TKey, TValue>
dan bungkus / delegasikan ke kamus dalam seperti yang disarankanICollection<KeyValuePair<TKey, TValue>>
set sebagai hanya baca atauIEnumerable<KeyValuePair<TKey, TValue>>
tergantung pada penggunaan nilai.ctor(IDictionary<TKey, TValue>)
dan mengembalikan salinan - dengan cara itu pengguna bebas untuk melakukannya sesuka mereka dan itu tidak berdampak pada keadaan objek hosting kamus sumber. Perhatikan bahwa jika kamus yang Anda kloning berisi jenis referensi (bukan string seperti yang ditunjukkan dalam contoh) Anda perlu menyalinnya secara "manual" dan mengkloning jenis referensi juga.Sebagai samping; ketika mengekspos koleksi, bertujuan untuk mengekspos antarmuka sekecil mungkin - dalam contoh kasus itu harus IDictionary karena ini memungkinkan Anda untuk memvariasikan implementasi yang mendasarinya tanpa melanggar kontrak publik yang diekspos oleh tipe tersebut.
sumber
Kamus read-only sampai batas tertentu dapat diganti dengan
Func<TKey, TValue>
- Saya biasanya menggunakan ini dalam API jika saya hanya ingin orang yang melakukan pencarian; itu sederhana, dan khususnya, mudah untuk mengganti backend jika Anda ingin. Namun, itu tidak memberikan daftar kunci; apakah itu penting tergantung pada apa yang Anda lakukan.sumber
Tidak, tetapi akan mudah untuk menggulung sendiri. IDictionary mendefinisikan properti IsReadOnly. Hanya membungkus Kamus dan melempar NotSupportedException dari metode yang sesuai.
sumber
Tidak ada yang tersedia di BCL. Namun saya menerbitkan ReadOnlyDictionary (bernama ImmutableMap) di Proyek BCL Extras saya
Selain menjadi kamus yang sepenuhnya tidak dapat diubah, kamus ini juga mendukung pembuatan objek proxy yang mengimplementasikan IDictionary dan dapat digunakan di mana pun tempat IDictionary diambil. Ini akan mengeluarkan pengecualian setiap kali salah satu API yang bermutasi dipanggil
sumber
Anda bisa membuat kelas yang hanya mengimplementasikan sebagian implementasi kamus, dan menyembunyikan semua fungsi add / remove / set.
Gunakan kamus secara internal bahwa kelas eksternal melewati semua permintaan.
Namun, karena kamus Anda kemungkinan memegang jenis referensi, tidak mungkin Anda dapat menghentikan pengguna dari menetapkan nilai pada kelas yang dipegang oleh kamus (kecuali jika kelas itu sendiri hanya dibaca)
sumber
Saya rasa tidak ada cara mudah untuk melakukannya ... jika kamus Anda adalah bagian dari kelas khusus, Anda dapat mencapainya dengan pengindeks:
sumber
+1 Pekerjaan bagus, Thomas. Saya mengambil ReadOnlyDictionary selangkah lebih maju.
Sama seperti solusi Dale, saya ingin menghapus
Add()
,Clear()
,Remove()
, dll dari IntelliSense. Tapi saya ingin objek turunan saya diimplementasikanIDictionary<TKey, TValue>
.Selain itu, saya ingin kode berikut untuk dipecah: (Sekali lagi, solusi Dale juga melakukan ini)
Baris Tambahkan () menghasilkan:
Penelepon masih dapat
IDictionary<TKey, TValue>
mengirimkannya, tetapiNotSupportedException
akan dimunculkan jika Anda mencoba menggunakan anggota yang tidak hanya baca (dari solusi Thomas).Bagaimanapun, inilah solusi saya untuk siapa saja yang juga menginginkan ini:
sumber
Sekarang, ada Microsoft Immutable Collections (
System.Collections.Immutable
). Dapatkan melalui NuGet .sumber
sumber
public IEnumerable<KeyValuePair<string, string>> MyDictionary() { return _mydictionary; }
Ini adalah solusi yang buruk, lihat di bagian bawah.
Bagi mereka yang masih menggunakan .NET 4.0 atau sebelumnya, saya memiliki kelas yang berfungsi seperti yang ada di jawaban yang diterima, tetapi jauh lebih pendek. Itu memperluas objek Kamus yang ada, menimpa (sebenarnya menyembunyikan) anggota tertentu agar mereka melemparkan pengecualian saat dipanggil.
Jika pemanggil mencoba memanggil Tambah, Hapus, atau operasi mutasi lain yang dimiliki Kamus bawaan, kompiler akan membuat kesalahan. Saya menggunakan atribut Usang untuk meningkatkan kesalahan kompilator ini. Dengan cara ini, Anda dapat mengganti Kamus dengan ReadOnlyDictionary ini dan segera melihat di mana masalah mungkin tanpa harus menjalankan aplikasi Anda dan menunggu pengecualian run-time.
Lihatlah:
Solusi ini memiliki masalah yang ditunjukkan oleh @supercat yang diilustrasikan di sini:
Daripada memberikan kesalahan waktu kompilasi seperti yang saya harapkan, atau pengecualian runtime seperti yang saya harapkan, kode ini berjalan tanpa kesalahan. Mencetak empat angka. Itu membuat ReadOnlyDictionary saya menjadi ReadWriteDictionary.
sumber
Dictionary<TKey,TValue>
tanpa kompiler komplain, dan melemparkan atau memaksa referensi ke tipe kamus sederhana akan menghapus perlindungan apa pun.Dictionary
denganClone
metode yang dirantaiMemberwiseClone
. Sayangnya, walaupun harus dimungkinkan untuk mengkloning kamus secara efisien dengan mengkloning toko-toko pendukung, fakta bahwa toko-toko pendukungprivate
bukanprotected
berarti tidak ada cara bagi kelas turunan untuk mengkloning mereka; menggunakanMemberwiseClone
tanpa juga mengkloning toko dukungan akan berarti bahwa modifikasi berikutnya yang dibuat untuk kamus asli akan merusak klon, dan modifikasi yang dilakukan pada klon akan merusak yang asli.