Jadi, apa sebenarnya kasus penggunaan yang baik untuk mengimplementasikan antarmuka secara eksplisit?
Apakah hanya agar orang yang menggunakan kelas tidak perlu melihat semua metode / properti tersebut di intellisense?
Jika Anda mengimplementasikan dua antarmuka, dengan metode yang sama dan implementasi yang berbeda, Anda harus mengimplementasikan secara eksplisit.
public interface IDoItFast
{
void Go();
}
public interface IDoItSlow
{
void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
void IDoItFast.Go()
{
}
void IDoItSlow.Go()
{
}
}
Ini berguna untuk menyembunyikan anggota yang tidak disukai. Misalnya, jika Anda menerapkan keduanya
IComparable<T>
danIComparable
biasanya lebih baik menyembunyikanIComparable
kelebihan beban agar tidak memberi kesan kepada orang-orang bahwa Anda dapat membandingkan objek dari jenis yang berbeda. Demikian pula, beberapa antarmuka tidak sesuai dengan CLS, misalnyaIConvertible
, jadi jika Anda tidak mengimplementasikan antarmuka secara eksplisit, pengguna akhir bahasa yang memerlukan kepatuhan CLS tidak dapat menggunakan objek Anda. (Yang akan sangat berbahaya jika pelaksana BCL tidak menyembunyikan anggota IConvertible dari primitif :))Catatan menarik lainnya adalah bahwa biasanya menggunakan konstruksi seperti itu berarti struct yang secara eksplisit mengimplementasikan antarmuka hanya dapat memanggilnya dengan meninju ke tipe antarmuka. Anda bisa menyiasatinya dengan menggunakan batasan umum:
Tidak akan mengemas int saat Anda memberikannya.
sumber
string
sudah ada sebelum obat generik dan praktik ini populer. Ketika .net 2 muncul, mereka tidak ingin merusak antarmuka publikstring
sehingga mereka membiarkannya seperti itu dengan pengamanan di tempatnya.Beberapa alasan tambahan untuk mengimplementasikan antarmuka secara eksplisit:
kompatibilitas mundur : Jika
ICloneable
antarmuka berubah, anggota kelas yang menerapkan metode tidak perlu mengubah tanda tangan metode mereka.kode pembersih : akan ada kesalahan kompiler jika
Clone
metode tersebut dihapus dari ICloneable, namun jika Anda menerapkan metode secara implisit, Anda dapat berakhir dengan metode publik 'orphaned' yang tidak terpakaipengetikan yang kuat : Untuk mengilustrasikan cerita supercat dengan sebuah contoh, ini akan menjadi kode sampel pilihan saya, menerapkan
ICloneable
secara eksplisit memungkinkanClone()
untuk diketik dengan kuat ketika Anda memanggilnya secara langsung sebagai anggotaMyObject
contoh:sumber
interface ICloneable<out T> { T Clone(); T self {get;} }
. Perhatikan bahwa tidak adaICloneable<T>
batasan pada T. Sementara sebuah objek umumnya hanya dapat dikloning dengan aman jika basisnya bisa, seseorang mungkin ingin mendapatkan dari kelas dasar yang dapat dengan aman mengkloning objek dari kelas yang tidak bisa. Untuk memungkinkan itu, saya merekomendasikan agar kelas yang diwariskan mengekspos metode klon publik. Sebaliknya memiliki kelas yang diwariskan denganprotected
metode kloning dan kelas tertutup yang berasal dari mereka dan mengekspos kloning publik.Teknik lain yang berguna adalah membuat implementasi publik suatu fungsi dari suatu metode mengembalikan nilai yang lebih spesifik daripada yang ditentukan dalam sebuah antarmuka.
Misalnya, sebuah objek dapat diimplementasikan
ICloneable
, tetapi masih memilikiClone
metode yang terlihat secara publik mengembalikan tipenya sendiri.Demikian juga,
IAutomobileFactory
mungkin memilikiManufacture
metode yang mengembalikanAutomobile
, tetapi aFordExplorerFactory
, yang mengimplementasikanIAutomobileFactory
, mungkin memilikiManufacture
metode mengembalikan aFordExplorer
(yang berasal dariAutomobile
). Kode yang mengetahui bahwa ia memilikiFordExplorerFactory
dapat menggunakanFordExplorer
properti -specific pada suatu objek yang dikembalikan oleh aFordExplorerFactory
tanpa harus melakukan typecast, sedangkan kode yang hanya mengetahui bahwa ia memiliki beberapa jenisIAutomobileFactory
akan menangani pengembaliannya sebagai fileAutomobile
.sumber
Ini juga berguna ketika Anda memiliki dua antarmuka dengan nama anggota dan tanda tangan yang sama, tetapi ingin mengubah perilakunya tergantung bagaimana penggunaannya. (Saya tidak merekomendasikan menulis kode seperti ini):
sumber
public class Animal : Cat, Dog
Hal ini dapat menjaga kebersihan antarmuka publik untuk mengimplementasikan antarmuka secara eksplisit, misalnya
File
kelas Anda mungkin mengimplementasikanIDisposable
secara eksplisit dan menyediakan metode publikClose()
yang mungkin lebih masuk akal bagi konsumen daripadaDispose(
).F # saja menawarkan implementasi antarmuka eksplisit sehingga Anda selalu harus mentransmisikan ke antarmuka tertentu untuk mengakses fungsinya, yang membuat penggunaan antarmuka menjadi sangat eksplisit (tanpa maksud kata-kata).
sumber
Dispose
adalah hal-hal yang tidak akan pernah membutuhkan pembersihan); contoh yang lebih baik adalah implementasi dari koleksi yang tidak dapat diubahIList<T>.Add
.Jika Anda memiliki antarmuka internal dan Anda tidak ingin menerapkan anggota di kelas Anda secara publik, Anda akan menerapkannya secara eksplisit. Implementasi implisit harus diketahui publik.
sumber
Alasan lain untuk implementasi eksplisit adalah untuk pemeliharaan .
Ketika kelas menjadi "sibuk" - ya itu terjadi, kita tidak semua memiliki kemewahan untuk memfaktorkan ulang kode anggota tim lain - kemudian memiliki implementasi eksplisit memperjelas bahwa ada metode di sana untuk memenuhi kontrak antarmuka.
Jadi itu meningkatkan "keterbacaan" kode.
sumber
#region
, dengan string judul yang sesuai. Dan komentar tentang metodenya.Contoh berbeda diberikan oleh
System.Collections.Immutable
, di mana penulis memilih untuk menggunakan teknik untuk mempertahankan API yang sudah dikenal untuk jenis koleksi sambil membuang bagian antarmuka yang tidak memiliki arti untuk jenis baru mereka.Secara konkret,
ImmutableList<T>
mengimplementasikanIList<T>
dan dengan demikianICollection<T>
( agar memungkinkanImmutableList<T>
untuk digunakan lebih mudah dengan kode warisan), namunvoid ICollection<T>.Add(T item)
tidak masuk akal untukImmutableList<T>
: karena menambahkan elemen ke daftar yang tidak dapat diubah tidak boleh mengubah daftar yang ada,ImmutableList<T>
juga berasal dariIImmutableList<T>
yangIImmutableList<T> Add(T item)
dapat digunakan untuk daftar yang tidak dapat diubah.Jadi dalam kasus
Add
, implementasi padaImmutableList<T>
akhirnya terlihat sebagai berikut:sumber
Dalam kasus antarmuka yang ditentukan secara eksplisit, semua metode secara otomatis menjadi pribadi, Anda tidak dapat memberikan pengubah akses publik kepada mereka. Seharusnya:
sumber
Beginilah cara kita membuat Antarmuka Eksplisit: Jika kita memiliki 2 antarmuka dan kedua antarmuka memiliki metode yang sama dan satu kelas mewarisi 2 antarmuka ini, sehingga ketika kita memanggil satu metode antarmuka, kompilator bingung metode mana yang akan dipanggil, jadi kita bisa mengelola masalah ini menggunakan Antarmuka Eksplisit. Berikut adalah salah satu contoh yang saya berikan di bawah ini.
sumber