Saya mengerti bagaimana cara bekerja dengan antarmuka dan implementasi antarmuka eksplisit dalam C #, tapi saya bertanya-tanya apakah itu dianggap bentuk yang buruk untuk menyembunyikan anggota tertentu yang tidak akan sering digunakan. Sebagai contoh:
public interface IMyInterface
{
int SomeValue { get; set; }
int AnotherValue { get; set; }
bool SomeFlag { get; set; }
event EventHandler SomeValueChanged;
event EventHandler AnotherValueChanged;
event EventHandler SomeFlagChanged;
}
Saya tahu sebelumnya bahwa acara, meskipun bermanfaat, akan sangat jarang digunakan. Jadi, saya pikir akan masuk akal untuk menyembunyikan mereka dari kelas implementasi melalui implementasi eksplisit untuk menghindari kekacauan IntelliSense.
c#
interfaces
implementations
Kyle Baran
sumber
sumber
Jawaban:
Ya, itu bentuk umumnya buruk.
Jika IntelliSense Anda terlalu berantakan, kelas Anda terlalu besar. Jika kelas Anda terlalu besar, kemungkinan melakukan terlalu banyak hal dan / atau dirancang dengan buruk.
Perbaiki masalah Anda, bukan gejala Anda.
sumber
Gunakan fitur untuk apa yang dimaksudkan untuk itu. Mengurangi kekacauan namespace bukan salah satunya.
Alasan yang sah bukan tentang "persembunyian" atau organisasi, itu untuk menyelesaikan ambiguitas dan untuk berspesialisasi. Jika Anda menerapkan dua antarmuka yang mendefinisikan "Foo", semantik untuk Foo mungkin berbeda. Benar-benar bukan cara yang lebih baik untuk menyelesaikan masalah ini kecuali untuk antarmuka eksplisit. Alternatifnya adalah melarang penerapan antarmuka yang tumpang tindih, atau menyatakan bahwa antarmuka tidak dapat mengaitkan semantik (yang sebenarnya hanyalah hal konseptual). Keduanya merupakan alternatif yang buruk. Terkadang kepraktisan mengalahkan keanggunan.
Beberapa penulis / ahli menganggapnya sebagai kludge dari suatu fitur (metode ini tidak benar-benar bagian dari model objek tipe ini). Tapi seperti semua fitur, di suatu tempat, seseorang membutuhkannya.
Sekali lagi, saya tidak akan menggunakannya untuk persembunyian atau organisasi. Ada cara untuk menyembunyikan anggota dari intellisense.
sumber
IDisposable.Dispose
melakukan sesuatu tetapi diberi nama yang berbeda sepertiClose
, saya akan menganggapnya masuk akal untuk kelas yang kontraknya secara tegas menyangkal menyatakan bahwa kode dapat membuat dan meninggalkan contoh tanpa memanggilDispose
untuk menggunakan implementasi antarmuka eksplisit untuk mendefinisikanIDisposable.Dispose
metode yang tidak melakukan apa-apa.Saya mungkin menjadi tanda kode berantakan, tetapi kadang-kadang dunia nyata berantakan. Salah satu contoh dari .NET framework adalah ReadOnlyColection yang menyembunyikan setter yang bermutasi [], Add and Set.
IE ReadOnlyCollection mengimplementasikan IList <T>. Lihat juga pertanyaan saya ke SO beberapa tahun yang lalu ketika saya merenungkan ini.
sumber
ConcurrentDictionary
, yang mengimplementasikan berbagai anggotaIDictionary<T>
secara eksplisit sehingga konsumenConcurrentDictionary
A) tidak akan memanggil mereka secara langsung dan B) dapat menggunakan metode yang memerlukan implementasiIDictionary<T>
jika benar-benar diperlukan. Misalnya, penggunaConcurrentDictionary<T>
harus meneleponTryAdd
daripadaAdd
menghindari perlu pengecualian yang tidak perlu.Add
secara eksplisit juga mengurangi kekacauan.TryAdd
dapat melakukan apa saja yangAdd
dapat dilakukan (Add
diimplementasikan sebagai panggilan untukTryAdd
, sehingga menyediakan keduanya akan berlebihan). Namun,TryAdd
tentu memiliki nama / tanda tangan yang berbeda, sehingga tidak mungkin diterapkanIDictionary
tanpa redundansi ini. Implementasi eksplisit menyelesaikan masalah ini dengan bersih.Ini adalah bentuk yang baik untuk melakukannya ketika anggota tidak ada artinya dalam konteks. Misalnya, jika Anda membuat koleksi hanya baca yang mengimplementasikan
IList<T>
dengan mendelegasikan ke objek internal_wrapped
maka Anda mungkin memiliki sesuatu seperti:Di sini kita punya empat kasus berbeda.
public T this[int index]
didefinisikan oleh kelas kita daripada antarmuka, dan karenanya tentu saja bukan implementasi eksplisit, meskipun perhatikan bahwa itu memang mirip dengan baca-tulis yangT this[int index]
didefinisikan dalam antarmuka tetapi hanya baca-saja.T IList<T>.this[int index]
eksplisit karena salah satu bagiannya (pengambil) sangat cocok dengan properti di atas, dan bagian lainnya akan selalu melemparkan pengecualian. Sementara penting bagi seseorang yang mengakses instance kelas ini melalui antarmuka, tidak ada gunanya bagi seseorang yang menggunakannya melalui variabel tipe kelas.Demikian pula karena
bool ICollection<T>.IsReadOnly
selalu akan mengembalikan true itu sama sekali tidak ada gunanya untuk kode yang ditulis terhadap tipe kelas, tetapi bisa sangat penting untuk menggunakannya melalui tipe antarmuka, dan oleh karena itu kami menerapkannya secara eksplisit.Sebaliknya,
public int Count
tidak diterapkan secara eksplisit karena berpotensi dapat digunakan untuk seseorang yang menggunakan instance melalui tipe sendiri.Tetapi dengan kasus "sangat jarang digunakan" Anda, saya akan cenderung sangat kuat untuk tidak menggunakan implementasi eksplisit.
Dalam kasus di mana saya merekomendasikan menggunakan implementasi eksplisit memanggil metode melalui variabel dari tipe kelas akan menjadi kesalahan (mencoba untuk menggunakan setter yang diindeks) atau tidak berguna (memeriksa nilai yang akan selalu sama) sehingga dalam bersembunyi mereka Anda melindungi pengguna dari kode kereta atau sub-optimal. Itu sangat berbeda dengan kode yang Anda pikir cenderung jarang digunakan. Untuk itu saya mungkin mempertimbangkan untuk menggunakan
EditorBrowsable
atribut untuk menyembunyikan anggota dari intellisense, meskipun bahkan saya akan bosan; otak orang sudah memiliki perangkat lunak mereka sendiri untuk menyaring apa yang tidak menarik bagi mereka.sumber
IsReadOnly
properti?