Apa perbedaan dalam mengimplementasikan antarmuka secara implisit dan eksplisit dalam C #?
Kapan Anda harus menggunakan implisit dan kapan Anda harus menggunakan eksplisit?
Apakah ada pro dan / atau kontra satu atau yang lain?
Pedoman resmi Microsoft (dari Pedoman Kerangka Desain edisi pertama ) menyatakan bahwa menggunakan implementasi eksplisit tidak dianjurkan , karena memberikan kode perilaku yang tidak terduga.
Saya pikir pedoman ini sangat valid di waktu pra-IoC , ketika Anda tidak membagikan hal-hal sebagai antarmuka.
Adakah yang bisa menyentuh aspek itu juga?
Jawaban:
Tersirat adalah ketika Anda mendefinisikan antarmuka Anda melalui anggota di kelas Anda. Eksplisit adalah ketika Anda mendefinisikan metode dalam kelas Anda pada antarmuka. Saya tahu itu terdengar membingungkan tetapi inilah yang saya maksudkan:
IList.CopyTo
secara implisit akan diterapkan sebagai:dan secara eksplisit sebagai:
Perbedaannya adalah implementasi implisit memungkinkan Anda untuk mengakses antarmuka melalui kelas yang Anda buat dengan melemparkan antarmuka sebagai kelas itu dan sebagai antarmuka itu sendiri. Implementasi eksplisit memungkinkan Anda untuk mengakses antarmuka hanya dengan melemparkannya sebagai antarmuka itu sendiri.
Saya menggunakan eksplisit terutama untuk menjaga implementasi bersih, atau ketika saya membutuhkan dua implementasi. Bagaimanapun, saya jarang menggunakannya.
Saya yakin ada lebih banyak alasan untuk menggunakan / tidak menggunakan secara eksplisit bahwa orang lain akan memposting.
Lihat posting berikutnya di utas ini untuk alasan yang sangat baik di balik masing-masing.
sumber
public
kata kunci ... kalau tidak, Anda akan mendapatkan kesalahanUISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }
.IEnumerator<T>.Current
,IEnumerable<T>.GetEnumerator()
, danISet<T>.Add(T)
. Ini disebutkan dalam jawaban lain .Definisi tersirat adalah dengan hanya menambahkan metode / properti, dll. Yang diminta oleh antarmuka langsung ke kelas sebagai metode publik.
Definisi eksplisit memaksa anggota untuk diekspos hanya ketika Anda bekerja dengan antarmuka secara langsung, dan bukan implementasi yang mendasarinya. Ini lebih disukai dalam banyak kasus.
sumber
Cat
memangIEatable
tanpa keberatan.Selain jawaban yang sangat baik yang telah disediakan, ada beberapa kasus di mana implementasi eksplisit DIBUTUHKAN untuk kompiler untuk dapat mengetahui apa yang diperlukan. Lihatlah
IEnumerable<T>
sebagai contoh utama yang kemungkinan akan cukup sering muncul.Ini sebuah contoh:
Di sini,
IEnumerable<string>
mengimplementasikanIEnumerable
, maka kita juga perlu melakukannya. Tapi tunggu sebentar, baik versi generik dan normal sama-sama mengimplementasikan fungsi dengan metode tanda tangan yang sama (C # mengabaikan tipe return untuk ini). Ini sepenuhnya sah dan sah. Bagaimana kompiler memutuskan mana yang akan digunakan? Ini memaksa Anda untuk hanya memiliki, paling banyak, satu definisi implisit, maka ia dapat menyelesaikan apa pun yang diperlukan.yaitu.
PS: Sepotong kecil tipuan dalam definisi eksplisit untuk IEnumerable berfungsi karena di dalam fungsi kompiler tahu bahwa tipe sebenarnya dari variabel adalah StringList, dan itulah cara menyelesaikan pemanggilan fungsi. Fakta kecil yang bagus untuk menerapkan beberapa lapisan abstraksi beberapa antarmuka inti .NET tampaknya telah terakumulasi.
sumber
abstract
.Mengutip Jeffrey Richter dari CLR via C #
( EIMI berarti E xplicit I nterface M ethod I mplementation )
Jika Anda menggunakan referensi antarmuka SETIAP rantai virtual dapat secara eksplisit diganti dengan EIMI pada kelas turunan mana pun dan ketika objek jenis tersebut dilemparkan ke antarmuka, rantai virtual Anda diabaikan dan implementasi eksplisit disebut. Itu sama sekali bukan polimorfisme.
EIMI juga dapat digunakan untuk menyembunyikan anggota antarmuka yang tidak diketik dengan kuat dari implementasi Framework Interfaces dasar seperti IEnumerable <T> sehingga kelas Anda tidak mengekspos metode yang tidak diketik secara langsung, tetapi secara sintaksis benar.
sumber
Alasan # 1
Saya cenderung menggunakan implementasi antarmuka eksplisit ketika saya ingin mencegah "pemrograman ke implementasi" ( Prinsip Desain dari Pola Desain ).
Misalnya, dalam aplikasi web berbasis MVP :
Sekarang kelas lain (seperti presenter ) cenderung bergantung pada implementasi StandardNavigator dan lebih cenderung bergantung pada antarmuka INavigator (karena implementasinya perlu dilemparkan ke antarmuka untuk menggunakan metode Redirect).
Alasan # 2
Alasan lain saya mungkin pergi dengan implementasi antarmuka eksplisit adalah untuk menjaga pembersih antarmuka "default" kelas. Misalnya, jika saya mengembangkan kontrol server ASP.NET , saya mungkin ingin dua antarmuka:
Contoh sederhana berikut. Ini adalah kotak kombo kontrol yang mencantumkan pelanggan. Dalam contoh ini, pengembang halaman web tidak tertarik untuk mengisi daftar; sebagai gantinya, mereka hanya ingin dapat memilih pelanggan dengan GUID atau untuk mendapatkan GUID pelanggan yang dipilih. Seorang presenter akan mengisi kotak pada halaman pertama, dan presenter ini dienkapsulasi oleh kontrol.
Presenter mengisi sumber data, dan pengembang halaman web tidak perlu menyadari keberadaannya.
Tapi Ini Bukan Meriam Perak
Saya tidak akan merekomendasikan selalu menggunakan implementasi antarmuka eksplisit. Itu hanya dua contoh di mana mereka mungkin bisa membantu.
sumber
Selain alasan lain yang telah dinyatakan, ini adalah situasi di mana kelas mengimplementasikan dua antarmuka berbeda yang memiliki properti / metode dengan nama dan tanda tangan yang sama.
Kode ini mengkompilasi dan menjalankan OK, tetapi properti Judul dibagikan.
Jelas, kami ingin nilai Judul dikembalikan tergantung pada apakah kami memperlakukan Class1 sebagai Buku atau Orang. Inilah saatnya kita dapat menggunakan antarmuka eksplisit.
Perhatikan bahwa definisi antarmuka eksplisit disimpulkan sebagai Publik - dan karenanya Anda tidak dapat mendeklarasikannya sebagai publik (atau sebaliknya) secara eksplisit.
Perhatikan juga bahwa Anda masih dapat memiliki versi "bersama" (seperti yang ditunjukkan di atas), tetapi sementara ini dimungkinkan, keberadaan properti seperti itu dipertanyakan. Mungkin ini dapat digunakan sebagai implementasi standar Judul - sehingga kode yang ada tidak harus dimodifikasi untuk memberikan Class1 ke IBook atau IPerson.
Jika Anda tidak mendefinisikan Judul "dibagikan" (implisit), konsumen Class1 harus secara eksplisit melemparkan instance dari Class1 ke IBook atau IPerson terlebih dahulu - jika kode tidak akan dikompilasi.
sumber
Saya menggunakan implementasi antarmuka eksplisit sebagian besar waktu. Inilah alasan utamanya.
Refactoring lebih aman
Saat mengganti antarmuka, lebih baik jika kompilator dapat memeriksanya. Ini lebih sulit dengan implementasi implisit.
Dua kasus umum muncul di pikiran:
Menambahkan fungsi ke antarmuka, di mana kelas yang sudah ada yang mengimplementasikan antarmuka ini sudah memiliki metode dengan tanda tangan yang sama dengan yang baru . Ini dapat menyebabkan perilaku yang tidak terduga, dan telah menggigit saya beberapa kali. Sulit untuk "melihat" ketika debugging karena fungsi itu kemungkinan tidak terletak dengan metode antarmuka lain dalam file (masalah mendokumentasikan diri disebutkan di bawah).
Menghapus fungsi dari antarmuka . Metode yang diimplementasikan secara implisit akan tiba-tiba mati kode, tetapi metode yang diimplementasikan secara eksplisit akan tertangkap oleh kesalahan kompilasi. Bahkan jika kode mati itu baik untuk disimpan, saya ingin dipaksa untuk memeriksanya dan mempromosikannya.
Sangat disayangkan bahwa C # tidak memiliki kata kunci yang memaksa kita untuk menandai metode sebagai implementasi implisit, sehingga kompiler dapat melakukan pemeriksaan tambahan. Metode virtual tidak memiliki salah satu masalah di atas karena diperlukan penggunaan 'menimpa' dan 'baru'.
Catatan: untuk antarmuka yang diperbaiki atau jarang berubah (biasanya dari vendor API), ini bukan masalah. Untuk antarmuka saya sendiri, saya tidak bisa memprediksi kapan / bagaimana mereka akan berubah.
Ini mendokumentasikan diri
Jika saya melihat 'public bool Execute ()' di kelas, itu akan membutuhkan kerja ekstra untuk mengetahui bahwa itu adalah bagian dari antarmuka. Seseorang mungkin harus mengomentari dengan mengatakannya, atau memasukkannya ke dalam kelompok implementasi antarmuka lain, semuanya di bawah suatu wilayah atau mengelompokkan komentar yang mengatakan "implementasi ITask". Tentu saja, itu hanya berfungsi jika header grup tidak offscreen ..
Sedangkan: 'bool ITask.Execute ()' jelas dan tidak ambigu.
Pemisahan implementasi antarmuka yang jelas
Saya menganggap antarmuka sebagai lebih 'publik' daripada metode publik karena mereka dibuat untuk mengekspos hanya sedikit area permukaan dari tipe beton. Mereka mengurangi tipe menjadi kapabilitas, perilaku, seperangkat sifat, dll. Dan dalam implementasinya, saya pikir ini berguna untuk menjaga pemisahan ini.
Ketika saya melihat melalui kode kelas, ketika saya menemukan implementasi antarmuka eksplisit, otak saya beralih ke mode "kontrak kode". Seringkali implementasi ini hanya diteruskan ke metode lain, tetapi kadang-kadang mereka akan melakukan pengecekan status / param tambahan, konversi parameter yang masuk agar lebih cocok dengan persyaratan internal, atau bahkan terjemahan untuk tujuan pembuatan versi (yaitu beberapa generasi antarmuka semua berjalan menuju implementasi umum).
(Saya menyadari bahwa publik juga merupakan kontrak kode, tetapi antarmuka jauh lebih kuat, terutama dalam basis kode berbasis antarmuka di mana penggunaan langsung tipe konkret biasanya merupakan tanda kode internal saja.)
Terkait: Alasan 2 di atas oleh Jon .
Dan seterusnya
Plus kelebihan yang telah disebutkan dalam jawaban lain di sini:
Masalah
Tidak semua kesenangan dan kebahagiaan. Ada beberapa kasus di mana saya tetap dengan implisit:
Juga, itu bisa menyebalkan untuk melakukan casting ketika Anda sebenarnya memiliki tipe beton dan ingin memanggil metode antarmuka eksplisit. Saya menangani ini dalam satu dari dua cara:
public IMyInterface I { get { return this; } }
(yang harus di sebaris) dan panggilanfoo.I.InterfaceMethod()
. Jika banyak antarmuka yang membutuhkan kemampuan ini, perluas nama di luar saya (dalam pengalaman saya jarang ada yang membutuhkan ini).sumber
Jika Anda menerapkan secara eksplisit, Anda hanya akan dapat merujuk anggota antarmuka melalui referensi yang bertipe antarmuka. Referensi yang merupakan tipe kelas pelaksana tidak akan mengekspos anggota antarmuka tersebut.
Jika kelas implementasi Anda tidak bersifat publik, kecuali metode yang digunakan untuk membuat kelas (yang bisa berupa pabrik atau wadah IoC ), dan kecuali untuk metode antarmuka (tentu saja), maka saya tidak melihat adanya keuntungan untuk mengimplementasikan secara eksplisit antarmuka.
Jika tidak, secara eksplisit mengimplementasikan antarmuka memastikan bahwa referensi ke kelas implementasi konkret Anda tidak digunakan, memungkinkan Anda untuk mengubah implementasi itu di lain waktu. "Pastikan", saya kira, adalah "keuntungan". Implementasi yang diperhitungkan dengan baik dapat mencapai ini tanpa implementasi eksplisit.
Kerugiannya, menurut saya, adalah Anda akan menemukan diri Anda mengetik tipe ke / dari antarmuka dalam kode implementasi yang memang memiliki akses ke anggota non-publik.
Seperti banyak hal, keuntungannya adalah kerugian (dan sebaliknya). Menerapkan antarmuka secara eksplisit akan memastikan bahwa kode implementasi kelas konkret Anda tidak terekspos.
sumber
Implementasi antarmuka implisit adalah tempat Anda memiliki metode dengan tanda tangan antarmuka yang sama.
Implementasi antarmuka eksplisit adalah tempat Anda secara eksplisit menyatakan antarmuka mana yang dimiliki metode ini.
MSDN: implementasi antarmuka implisit dan eksplisit
sumber
Setiap anggota kelas yang mengimplementasikan antarmuka mengekspor deklarasi yang secara semantik mirip dengan cara deklarasi antarmuka VB.NET ditulis, misalnya
Meskipun nama anggota kelas akan sering cocok dengan nama anggota antarmuka, dan anggota kelas akan sering bersifat publik, tidak satu pun dari hal-hal tersebut diperlukan. Seseorang juga dapat mendeklarasikan:
Dalam hal ini kelas dan turunannya akan diizinkan untuk mengakses anggota kelas menggunakan nama tersebut
IFoo_Foo
, tetapi dunia luar hanya akan dapat mengakses anggota tertentu dengan melakukan castingIFoo
. Pendekatan semacam itu sering baik dalam kasus di mana metode antarmuka akan memiliki perilaku yang ditentukan pada semua implementasi, tetapi perilaku yang berguna hanya pada beberapa [misalnya perilaku yang ditentukan untukIList<T>.Add
metode koleksi read-only adalah untuk membuangNotSupportedException
]. Sayangnya, satu-satunya cara yang tepat untuk mengimplementasikan antarmuka di C # adalah:Tidak sebagus itu.
sumber
Salah satu penggunaan penting dari implementasi antarmuka eksplisit adalah ketika perlu mengimplementasikan antarmuka dengan visibilitas campuran .
Masalah dan solusinya dijelaskan dengan baik di artikel C # Antarmuka Internal .
Misalnya, jika Anda ingin melindungi kebocoran objek di antara lapisan aplikasi, teknik ini memungkinkan Anda untuk menentukan visibilitas anggota yang berbeda yang dapat menyebabkan kebocoran.
sumber
Jawaban sebelumnya menjelaskan mengapa mengimplementasikan antarmuka secara eksplisit dalam C # mungkin lebih disukai (karena sebagian besar alasan formal). Namun, ada satu situasi di mana implementasi eksplisit adalah wajib : Untuk menghindari kebocoran enkapsulasi ketika antarmuka non-
public
, tetapi kelas pelaksanapublic
.Kebocoran di atas tidak dapat dihindari karena, menurut spesifikasi C # , "Semua anggota antarmuka secara implisit memiliki akses publik." Sebagai akibatnya, implementasi implisit juga harus memberikan
public
akses, bahkan jika antarmuka itu sendiri misalnyainternal
.Implementasi antarmuka implisit dalam C # adalah kenyamanan. Dalam praktiknya, banyak programmer menggunakannya sepanjang waktu / di mana saja tanpa pertimbangan lebih lanjut. Hal ini menyebabkan permukaan jenis berantakan paling buruk dan enkapsulasi bocor paling buruk. Bahasa lain, seperti F #, bahkan tidak mengizinkannya .
sumber