C # Antarmuka. Implementasi implisit versus implementasi Eksplisit

632

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?

Seb Nilsson
sumber
Baca Artikel Lengkap tentang antarmuka C #: planetofcoders.com/c-interfaces
Gaurav Agrawal
Ya, antarmuka eksplisit harus dihindari dan pendekatan yang lebih profesional akan menerapkan ISP (prinsip pemisahan Interface) di sini adalah artikel detail pada yang sama codeproject.com/Articles/1000374/...
Shivprasad Koirala

Jawaban:

492

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.CopyTosecara implisit akan diterapkan sebagai:

public void CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

dan secara eksplisit sebagai:

void ICollection.CopyTo(Array array, int index)
{
    throw new NotImplementedException();
}

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.

MyClass myClass = new MyClass(); // Declared as concrete class
myclass.CopyTo //invalid with explicit
((IList)myClass).CopyTo //valid with explicit.

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.

mattlant
sumber
8
Saya tahu posting ini sudah lama tetapi saya merasa sangat berguna - satu hal yang perlu diperhatikan jika tidak jelas karena bukan bagi saya adalah bahwa dalam contoh yang implisit memiliki publickata kunci ... kalau tidak, Anda akan mendapatkan kesalahan
jharr100
CLR Jeffrey Richter via C # 4 ed ch 13 menunjukkan contoh di mana casting tidak diperlukan: struct internal SomeValueType: IComparable {private Int32 m_x; SomeValueType publik (Int32 x) {m_x = x; } public Int32 CompareTo (SomeValueType other) {...);} Int32 IComparable.CompareTo (Object other) {return CompareTo ((SomeValueType) lainnya); }} public static void Main () {SomeValueType v = SomeValueType baru (0); Objek o = Objek baru (); Int32 n = v.CompareTo (v); // Tidak ada tinju n = v.CompareTo (o); // kesalahan waktu kompilasi}
Andy Dent
1
Hari ini saya menjumpai situasi langka yang MEMBUTUHKAN penggunaan antarmuka eksplisit: Kelas dengan bidang yang dibuat oleh pembuat antarmuka yang membuat bidang tersebut bersifat pribadi (Xamarin menargetkan iOS, menggunakan papan cerita iOS). Dan sebuah antarmuka di mana masuk akal untuk mengekspos bidang itu (readonly publik). Saya bisa saja mengubah nama pengambil di antarmuka, tetapi nama yang ada adalah nama yang paling logis untuk objek. Jadi, bukannya saya melakukan sebuah implementasi eksplisit mengacu pada bidang swasta: UISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }.
ToolmakerSteve
1
Kengerian pemrograman. Yah, dibalas dengan baik!
Liquid Core
1
@ToolmakerSteve situasi lain (agak lebih umum) yang memerlukan implementasi eksplisit dari setidaknya satu anggota antarmuka menerapkan beberapa antarmuka yang memiliki anggota dengan tanda tangan yang sama tetapi jenis pengembalian yang berbeda. Hal ini dapat terjadi karena warisan antarmuka, seperti halnya dengan IEnumerator<T>.Current, IEnumerable<T>.GetEnumerator(), dan ISet<T>.Add(T). Ini disebutkan dalam jawaban lain .
phoog
201

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.

  1. Dengan bekerja secara langsung dengan antarmuka, Anda tidak mengakui, dan menggabungkan kode Anda dengan implementasi yang mendasarinya.
  2. Jika Anda sudah memiliki, misalnya, Nama properti publik dalam kode Anda dan Anda ingin mengimplementasikan antarmuka yang juga memiliki properti Nama, melakukannya secara eksplisit akan membuat keduanya terpisah. Bahkan jika mereka melakukan hal yang sama, saya masih mendelegasikan panggilan eksplisit ke properti Name. Anda tidak pernah tahu, Anda mungkin ingin mengubah cara kerja Nama untuk kelas normal dan bagaimana Nama, properti antarmuka bekerja nanti.
  3. Jika Anda mengimplementasikan antarmuka secara implisit maka kelas Anda sekarang memperlihatkan perilaku baru yang mungkin hanya relevan untuk klien antarmuka dan itu berarti Anda tidak menjaga kelas Anda cukup ringkas (pendapat saya).
Phil Bennett
sumber
2
Anda membuat beberapa poin bagus di sini. terutama A. saya biasanya lulus kelas saya sebagai antarmuka, tapi saya tidak pernah benar-benar berpikir tentang hal itu membentuk perspektif itu.
mattlant
5
Saya tidak yakin saya setuju dengan poin C. Objek Cat mungkin mengimplementasikan IEatable tetapi Eat () adalah bagian dasar dari semuanya. Akan ada beberapa kasus di mana Anda hanya ingin memanggil Eat () pada Kucing ketika Anda menggunakan objek 'mentah' daripada melalui antarmuka IEatable, bukan?
LegendLength
59
Saya tahu beberapa tempat di mana Catmemang IEatabletanpa keberatan.
Humberto
26
Saya sama sekali tidak setuju dengan semua hal di atas, dan akan mengatakan bahwa menggunakan antarmuka eksplisit adalah resep bencana dan bukan OOP atau OOD menurut definisi mereka (lihat jawaban saya tentang polimorfisme)
Valentin Kuzub
2
Lihat Juga: stackoverflow.com/questions/4103300/…
Zack Jannsen
68

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:

public abstract class StringList : IEnumerable<string>
{
    private string[] _list = new string[] {"foo", "bar", "baz"};

    // ...

    #region IEnumerable<string> Members
    public IEnumerator<string> GetEnumerator()
    {
        foreach (string s in _list)
        { yield return s; }
    }
    #endregion

    #region IEnumerable Members
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
    #endregion
}

Di sini, IEnumerable<string>mengimplementasikan IEnumerable, 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.

StringList sl = new StringList();

// uses the implicit definition.
IEnumerator<string> enumerableString = sl.GetEnumerator();
// same as above, only a little more explicit.
IEnumerator<string> enumerableString2 = ((IEnumerable<string>)sl).GetEnumerator();
// returns the same as above, but via the explicit definition
IEnumerator enumerableStuff = ((IEnumerable)sl).GetEnumerator();

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.

Matthew Scharley
sumber
5
@Tassadaque: Setelah 2 tahun, yang bisa saya katakan adalah "Pertanyaan bagus". Saya tidak tahu, kecuali mungkin saya menyalin kode itu dari sesuatu yang saya kerjakan di tempat itu abstract.
Matthew Scharley
4
@Tassadaque, Anda benar, tapi saya pikir maksud posting Matthew Scharley di atas tidak hilang.
funkymushroom
35

Mengutip Jeffrey Richter dari CLR via C #
( EIMI berarti E xplicit I nterface M ethod I mplementation )

Sangat penting bagi Anda untuk memahami beberapa konsekuensi yang ada saat menggunakan EIMI. Dan karena konsekuensi ini, Anda harus berusaha menghindari EIMI sebanyak mungkin. Untungnya, antarmuka umum membantu Anda menghindari sedikit EIMI. Tetapi mungkin masih ada saatnya Anda perlu menggunakannya (seperti menerapkan dua metode antarmuka dengan nama dan tanda tangan yang sama). Inilah masalah besar dengan EIMIs:

  • Tidak ada dokumentasi yang menjelaskan bagaimana suatu tipe khusus mengimplementasikan metode EIMI, dan tidak ada dukungan Microsoft Visual Studio IntelliSense .
  • Instans tipe nilai dikotakkan ketika dilemparkan ke antarmuka.
  • EIMI tidak bisa dipanggil dengan tipe turunan.

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.

Valentin Kuzub
sumber
2
Implementasi ulang antarmuka, meskipun legal, umumnya meragukan. Implementasi eksplisit umumnya harus berantai ke metode virtual baik secara langsung, atau melalui logika pembungkus yang harus mengikat kelas turunan. Meskipun tentu saja mungkin untuk menggunakan antarmuka dengan cara yang memusuhi konvensi OOP yang tepat, itu tidak berarti mereka tidak dapat digunakan dengan lebih baik.
supercat
4
@Valentin Apa kepanjangan dari EIMI dan IEMI?
Dzienny
Implementasi Metode Antarmuka Eksplisit
scobi
5
-1 untuk "Secara umum saya melihat Antarmuka sebagai fitur OOP Semi (paling bagus), ia menyediakan warisan, tetapi itu tidak memberikan polimorfisme nyata." Saya sangat tidak setuju. Sebaliknya, antarmuka semua tentang polimorfisme dan bukan terutama tentang warisan. Mereka menganggap beberapa klasifikasi untuk suatu tipe. Hindari IEMI, ketika Anda bisa, dan delegasikan, seperti yang disarankan @supercat, ketika Anda tidak bisa. Jangan menghindari antarmuka.
Aluan Haddad
1
"EIMI tidak bisa dipanggil dengan tipe turunan." << APA? Itu tidak benar. Jika saya secara eksplisit mengimplementasikan antarmuka pada suatu jenis, maka saya berasal dari jenis itu, saya masih bisa melemparkannya ke antarmuka untuk memanggil metode, persis seperti yang saya harus untuk jenis itu diimplementasikan. Jadi tidak yakin apa yang Anda bicarakan. Bahkan di dalam tipe turunan, saya bisa dengan mudah memberikan "ini" ke antarmuka yang dimaksud untuk mencapai metode yang diterapkan secara eksplisit.
Triynko
34

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 :

public interface INavigator {
    void Redirect(string url);
}

public sealed class StandardNavigator : INavigator {
    void INavigator.Redirect(string url) {
        Response.Redirect(url);
    }
}

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:

  1. Antarmuka utama kelas, yang digunakan oleh pengembang halaman web; dan
  2. Antarmuka "tersembunyi" yang digunakan oleh presenter yang saya kembangkan untuk menangani logika kontrol

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.

public sealed class CustomerComboBox : ComboBox, ICustomerComboBox {
    private readonly CustomerComboBoxPresenter presenter;

    public CustomerComboBox() {
        presenter = new CustomerComboBoxPresenter(this);
    }

    protected override void OnLoad() {
        if (!Page.IsPostBack) presenter.HandleFirstLoad();
    }

    // Primary interface used by web page developers
    public Guid ClientId {
        get { return new Guid(SelectedItem.Value); }
        set { SelectedItem.Value = value.ToString(); }
    }

    // "Hidden" interface used by presenter
    IEnumerable<CustomerDto> ICustomerComboBox.DataSource { set; }
}

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.

Jon Nadal
sumber
19

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.

/// <summary>
/// This is a Book
/// </summary>
interface IBook
{
    string Title { get; }
    string ISBN { get; }
}

/// <summary>
/// This is a Person
/// </summary>
interface IPerson
{
    string Title { get; }
    string Forename { get; }
    string Surname { get; }
}

/// <summary>
/// This is some freaky book-person.
/// </summary>
class Class1 : IBook, IPerson
{
    /// <summary>
    /// This method is shared by both Book and Person
    /// </summary>
    public string Title
    {
        get
        {
            string personTitle = "Mr";
            string bookTitle = "The Hitchhikers Guide to the Galaxy";

            // What do we do here?
            return null;
        }
    }

    #region IPerson Members

    public string Forename
    {
        get { return "Lee"; }
    }

    public string Surname
    {
        get { return "Oades"; }
    }

    #endregion

    #region IBook Members

    public string ISBN
    {
        get { return "1-904048-46-3"; }
    }

    #endregion
}

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.

string IBook.Title
{
    get
    {
        return "The Hitchhikers Guide to the Galaxy";
    }
}

string IPerson.Title
{
    get
    {
        return "Mr";
    }
}

public string Title
{
    get { return "Still shared"; }
}

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.

Lee Oades
sumber
16

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:

  • Jenis nilai, karena itu akan membutuhkan tinju dan perf yang lebih rendah. Ini bukan aturan ketat, dan tergantung pada antarmuka dan bagaimana itu dimaksudkan untuk digunakan. Sebanding? Implisit. IFattattable? Mungkin eksplisit.
  • Antarmuka sistem trivial yang memiliki metode yang sering disebut langsung (seperti IDisposable.Dispose).

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:

  1. Tambahkan publik dan minta metode antarmuka untuk menerapkannya. Biasanya terjadi dengan antarmuka yang lebih sederhana ketika bekerja secara internal.
  2. (Metode pilihan saya) Tambahkan public IMyInterface I { get { return this; } }(yang harus di sebaris) dan panggilan foo.I.InterfaceMethod(). Jika banyak antarmuka yang membutuhkan kemampuan ini, perluas nama di luar saya (dalam pengalaman saya jarang ada yang membutuhkan ini).
scobi
sumber
8

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.

Tagihan
sumber
1
Jawaban yang bagus, Bill. Jawaban lainnya bagus, tetapi Anda memberikan beberapa sudut pandang objektif tambahan (di atas pendapat Anda) yang membuatnya lebih mudah bagi saya untuk memahami. Seperti kebanyakan hal, ada pro dan kontra dengan implementasi implisit atau eksplisit sehingga Anda hanya perlu menggunakan yang terbaik untuk skenario atau kasus penggunaan tertentu. Saya akan mengatakan mereka yang mencoba mencari tahu dengan lebih baik akan mendapat manfaat dari membaca jawaban Anda.
Daniel Eagle
6

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.

interface I1
{
    void implicitExample();
}

interface I2
{
    void explicitExample();
}


class C : I1, I2
{
    void implicitExample()
    {
        Console.WriteLine("I1.implicitExample()");
    }


    void I2.explicitExample()
    {
        Console.WriteLine("I2.explicitExample()");
    }
}

MSDN: implementasi antarmuka implisit dan eksplisit

Yochai Timmer
sumber
6

Setiap anggota kelas yang mengimplementasikan antarmuka mengekspor deklarasi yang secara semantik mirip dengan cara deklarasi antarmuka VB.NET ditulis, misalnya

Public Overridable Function Foo() As Integer Implements IFoo.Foo

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:

Protected Overridable Function IFoo_Foo() As Integer Implements IFoo.Foo

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 casting IFoo. 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 untuk IList<T>.Addmetode koleksi read-only adalah untuk membuang NotSupportedException]. Sayangnya, satu-satunya cara yang tepat untuk mengimplementasikan antarmuka di C # adalah:

int IFoo.Foo() { return IFoo_Foo(); }
protected virtual int IFoo_Foo() { ... real code goes here ... }

Tidak sebagus itu.

supercat
sumber
2

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.

nrodic
sumber
2

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 pelaksana public.

// Given:
internal interface I { void M(); }

// Then explicit implementation correctly observes encapsulation of I:
// Both ((I)CExplicit).M and CExplicit.M are accessible only internally.
public class CExplicit: I { void I.M() { } }

// However, implicit implementation breaks encapsulation of I, because
// ((I)CImplicit).M is only accessible internally, while CImplicit.M is accessible publicly.
public class CImplicit: I { public void M() { } }

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 publicakses, bahkan jika antarmuka itu sendiri misalnya internal.

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 .

Marc Sigrist
sumber