C # Arayüzler. Açık uygulama karşısında Açık uygulama


632

Arabirimleri C # içerisindeki dolaylı ve açık bir şekilde uygulamadaki farklar nelerdir ?

Ne zaman örtük, ne zaman örtük kullanmalısınız?

Birinin diğerine artıları ve / veya eksileri var mı?


Microsoft'un resmi yönergeleri (ilk sürümden Çerçeve Tasarım Yönergeleri'nden ) , kodda beklenmedik davranışlar sağladığından, açık uygulamaların kullanılmasının önerilmediğini belirtir .

Bu yönerge, şeyleri arayüz olarak iletmediğinizde IoC öncesi bir dönemde çok geçerli olduğunu düşünüyorum .

Birisi bu konuya da değinebilir mi?


C # arabirimleriyle ilgili tam makaleyi okuyun: planetofcoders.com/c-interfaces
Gaurav Agrawal

Evet, aynı bir detay makale açık arayüzler kaçınılmalıdır ve daha profesyonel bir yaklaşım burada ISS (Arayüz ayrımı ilkesini) uygulamaya olur codeproject.com/Articles/1000374/...
Shivprasad Koirala

Yanıtlar:


492

Örtük , arayüzünüzü sınıfınızdaki bir üye aracılığıyla tanımladığınız zamandır . Açık Eğer arayüzüyle ilgili sınıf içinde tanımlarsınız zamandır. Kulağa kafa karıştırıcı geliyor biliyorum ama demek IList.CopyToistediğim şu: örtük olarak uygulanacak:

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

ve açıkça:

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

Aradaki fark, örtük uygulamanın, arabirimi bu sınıf ve arabirimin kendisi olarak yayınlayarak oluşturduğunuz sınıf aracılığıyla arabirime erişmenize izin vermesidir. Açık uygulama, yalnızca arabirimin kendisi olarak yayınlayarak arabirime erişmenizi sağlar.

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

Uygulamayı temiz tutmak için veya iki uygulamaya ihtiyacım olduğunda açık bir şekilde kullanıyorum. Ne olursa olsun, nadiren kullanıyorum.

Eminim başkalarının göndereceği açık kullanmak veya kullanmamak için daha fazla neden vardır.

Her birinin arkasındaki mükemmel akıl yürütme için bu konudaki bir sonraki gönderiye bakın .


8
Bu yazının eski olduğunu biliyorum ama çok yararlı buldum - bana göre olmadığı için açık değilse not edilmesi gereken bir şey, örnekte örtük olanın publicanahtar kelimeye sahip olmasıydı ... aksi takdirde bir hata alacaksınız
jharr100

Jeffrey Richter'ın C # 4 ed ch 13 yoluyla CLR'si dökümün gerekli olmadığı bir örneği gösterir: iç yapı SomeValueType: IComparable {private Int32 m_x; public SomeValueType (Int32 x) {m_x = x; } public Int32 CompareTo (SomeValueType diğer) {...);} Int32 IComparable.CompareTo (Object other) {return CompareTo ((SomeValueType) diğer); }} public static void Main () {SomeValueType v = yeni SomeValueType (0); Object o = yeni Object (); Int32 n = v.CompareTo (v); // Boks yok n = v.CompareTo (o); // derleme zamanı hatası}
Andy Dent

1
Bugün, açık bir arabirim kullanımını GEREKEN nadir bir durumla karşılaştım: Alanı özel olarak oluşturan bir arabirim oluşturucu tarafından oluşturulan bir alana sahip bir sınıf (Xamarin, iOS film şeridini kullanarak iOS'u hedefliyor). Ve bu alanı ortaya çıkarmanın mantıklı olduğu bir arayüz (salt okunur). Arayüzdeki alıcının adını değiştirebilirdim, ancak mevcut ad nesne için en mantıklı addı. Yani bunun yerine özel alanına istinaden açık uygulama yaptı: UISwitch IScoreRegPlayerViewCell.markerSwitch { get { return markerSwitch; } }.
ToolmakerSteve

1
Programlamanın dehşeti. Güzel, çürütülmüş!
Sıvı Çekirdek

1
En az bir arabirim üyesinin açık bir şekilde uygulanmasını gerektiren başka bir durum (daha yaygın), aynı imzalı ancak farklı dönüş türlerine sahip üyeleri olan birden çok arabirim uyguluyor. Bu olanın aksine, çünkü arayüz miras olabilir IEnumerator<T>.Current, IEnumerable<T>.GetEnumerator()ve ISet<T>.Add(T). Bu başka bir cevapta belirtilmiştir .
phoog

201

Örtük tanım, arabirim tarafından talep edilen yöntemleri / özellikleri vb. Doğrudan sınıfa genel yöntemler olarak eklemek olacaktır.

Açık tanım, üyeleri yalnızca arabirimle çalışırken temel uygulama değil, yalnızca doğrudan çalışırken açıkta kalmaya zorlar. Çoğu durumda bu tercih edilir.

  1. Doğrudan arabirimle çalışarak, kodu kabul etmiyor ve kodunuzu temeldeki uygulamaya eşlemiyorsunuz.
  2. Kodunuzda zaten bir ortak mülk Adı olması ve ayrıca bir Ad özelliğine sahip bir arabirim uygulamak istiyorsanız, bunu açıkça yapmak iki ayrı ayrı kalır. Aynı şeyi yapsalar bile, açıkça Name özelliğine yapılan çağrıyı devredebilirim. Asla bilemezsiniz, Name'in normal sınıf için nasıl çalışacağını ve Interface özelliğinin daha sonra nasıl çalışacağını değiştirmek isteyebilirsiniz.
  3. Bir arabirimi dolaylı olarak uygularsanız, sınıfınız artık yalnızca arabirimin bir istemcisiyle ilgili olabilecek yeni davranışlar ortaya çıkarır ve bu da sınıflarınızı yeterince özlemediğiniz anlamına gelir (benim düşüncem).

2
Burada bazı iyi noktalara değindiniz. özellikle A. sınıflarımı her zaman arayüz olarak geçiririm, ama bu perspektifi hiç düşünmedim.
mattlant

5
C noktasıyla hemfikir olduğumdan emin değilim. Bir Cat nesnesi IEatable uygulayabilir, ancak Eat () işin temel bir parçasıdır. IEatable arabirimi yerine 'raw' nesnesini kullandığınızda bir Cat'te Eat () işlevini çağırmak istediğiniz durumlar olur, değil mi?
LegendLength

59
CatGerçekten IEatableitirazları olmayan bazı yerler biliyorum .
Humberto

26
Yukarıdakilerin hepsine tamamen katılmıyorum ve açık ara yüzleri kullanmanın tanımlarına göre OOP veya OOD değil bir felaket tarifi olduğunu söyleyebilirim (bkz. Polimorfizm hakkındaki cevabım)
Valentin Kuzub


68

Zaten sağlanan mükemmel cevaplara ek olarak, derleyicinin neyin gerekli olduğunu anlayabilmesi için açık uygulamanın GEREKLİ olduğu bazı durumlar vardır. IEnumerable<T>Muhtemelen oldukça sık ortaya çıkacak ana örnek olarak bir göz atın .

İşte bir örnek:

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
}

Burada, IEnumerable<string>uygular IEnumerable, bu yüzden de ihtiyacımız var. Ancak bekleyin, hem genel hem de normal sürüm aynı yöntem imzasına sahip işlevleri uygular (C # bunun için dönüş türünü yoksayar). Bu tamamen yasal ve iyi. Derleyici hangisinin kullanılacağını nasıl çözer? Sizi en fazla bir örtülü tanıma sahip olmaya zorlar, o zaman ihtiyacı olan her şeyi çözebilir.

yani.

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: IEnumerable için açık tanımdaki küçük dolaylı parça çalışır çünkü derleyici fonksiyonun içinde değişkenin gerçek tipinin bir StringList olduğunu bilir ve fonksiyon çağrısını bu şekilde çözer. Bazı .NET çekirdek arabirimlerinin soyutlama katmanlarının bazılarını uygulamak için çok küçük bir gerçek birikmiş görünmektedir.


5
@Tassadaque: 2 yıl sonra söyleyebileceğim tek şey "İyi soru". Bu kodu nerede çalıştığım bir şeyden kopyaladığım dışında hiçbir fikrim yok abstract.
Matthew Scharley

4
@Tassadaque, haklısın, ama bence Matthew Scharley'nin yukarıdaki yazısının noktası kaybolmadı.
funkymushroom

35

C # ile CLR Jeffrey Richter alıntı
( EIMI anlamına e Xplicit I nterface M METOT I mplementation)

EIMI'leri kullanırken var olan bazı sonuçları anlamanız kritik önem taşır. Ve bu sonuçlar nedeniyle, EIMI'lerden mümkün olduğunca kaçınmaya çalışmalısınız. Neyse ki, jenerik arayüzler EIMI'lerden biraz kaçınmanıza yardımcı olur. Ancak yine de bunları kullanmanız gereken zamanlar olabilir (aynı ad ve imzayla iki arabirim yöntemi uygulamak gibi). İşte EIMI'lerle ilgili büyük problemler:

  • Bir türün özel olarak bir EIMI yöntemini nasıl uyguladığını açıklayan herhangi bir belge yoktur ve Microsoft Visual Studio IntelliSense desteği yoktur.
  • Değer türü örnekleri, bir arabirime yayınlandığında kutu içine alınır.
  • EIMI türetilmiş bir tür tarafından çağrılamaz.

Bir arabirim referansı kullanırsanız, herhangi bir türetilmiş sınıfta herhangi bir sanal zincir açıkça EIMI ile değiştirilebilir ve bu tür bir nesne arabirime yayınlandığında, sanal zinciriniz yok sayılır ve açık uygulama çağrılır. Bu polimorfizmden başka bir şey değil.

EIMI'ler, güçlü yazılmamış arabirim üyelerini IEnumerable <T> gibi temel Çerçeve Arabirimleri uygulamalarından gizlemek için de kullanılabilir, böylece sınıfınız doğrudan yazılmamış bir yöntemi doğrudan göstermez, ancak sözdizimsel doğrudur.


2
Arayüzlerin yasal olmasına rağmen yeniden uygulanması genellikle en iyi ihtimalle şüphelidir. Açık uygulamalar genellikle doğrudan ya da türetilmiş sınıflar üzerinde bağlayıcı olması gereken sarma mantığı yoluyla sanal bir yönteme zincirlenmelidir. Arabirimleri uygun OOP kurallarına düşmanca bir şekilde kullanmak kesinlikle mümkün olsa da, bu daha iyi kullanılamayacakları anlamına gelmez.
supercat

4
@Valentin EIMI ve IEMI ne anlama geliyor?
Dzienny

Açık Arayüz Yöntemi Uygulaması
scobi

5
-1 için "Genellikle Arabirimleri Yarı (en iyi) OOP özelliği olarak görüyorum, kalıtım sağlar, ancak gerçek bir polimorfizm sağlamaz." Kesinlikle katılmıyorum. Aksine, arayüzler tamamen polimorfizmle ilgilidir ve öncelikle kalıtımla ilgili değildir. Bir türe birden çok sınıflandırma atfederler. Yapabildiğiniz zaman IEMI'den kaçının ve @supercat'in önerdiği gibi yapamadığınızda temsilci verin. Arabirimlerden kaçının.
Aluan Haddad

1
"EIMI türetilmiş bir tür tarafından çağrılamaz." << NE? Bu doğru değil. Açıkça bir tür üzerinde bir arabirim uygulamak, o türden türetmek, ben hala tam olarak uygulandığı tür için olurdu yöntemi yöntemi çağırmak için arabirime döküm olabilir. Neden bahsettiğinden emin değilim. Türetilmiş tipte bile, açıkça uygulanan yönteme ulaşmak için söz konusu arabirime "bu" yı atabilirim.
Triynko

34

Sebep # 1

"Programlamayı bir uygulamaya" ( Tasarım Desenlerinden Tasarım İlkeleri ) caydırmak istediğimde açık arabirim uygulamasını kullanma eğilimindeyim .

Örneğin, MVP tabanlı bir web uygulamasında:

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

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

Şimdi başka bir sınıf ( sunucu gibi) ) StandardNavigator uygulamasına bağımlı olma olasılığı daha düşüktür ve INavigator arabirimine bağımlı olma olasılığı daha yüksektir (çünkü uygulamanın Yönlendirme yöntemini kullanmak için bir arabirime aktarılması gerekir).

Sebep # 2

Açık bir arabirim uygulaması ile gidebilir başka bir nedeni, bir sınıfın "varsayılan" arabirimi temiz tutmak olacaktır. Örneğin, bir ASP.NET sunucu denetimi geliştiriyorsam , iki arabirim isteyebilirim:

  1. Web sayfası geliştiricileri tarafından kullanılan sınıfın birincil arayüzü; ve
  2. Kontrolün mantığını işlemek için geliştirdiğim sunucu tarafından kullanılan "gizli" bir arayüz

Basit bir örnek aşağıdadır. Müşterileri listeleyen bir açılan kutu kontrolüdür. Bu örnekte, web sayfası geliştiricisi listeyi doldurmakla ilgilenmez; bunun yerine, sadece GUID ile bir müşteri seçebilmek veya seçilen müşterinin GUID'sini elde etmek istiyorlar. Bir sunucu, ilk sayfa yüklemesinde kutuyu doldurur ve bu sunucu, denetim tarafından kapsüllenir.

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; }
}

Sunucu veri kaynağını doldurur ve web sayfası geliştiricisinin varlığından asla haberdar olması gerekmez.

Ama bu bir Gümüş Top mermisi değil

Her zaman açık arabirim uygulamaları kullanmanızı önermem. Bunlar yardımcı olabilecekleri sadece iki örnektir.


19

Daha önce belirtilen diğer nedenlere ek olarak, bu, bir sınıfın aynı ada ve imzayla bir özelliği / yöntemi olan iki farklı arabirimi uyguladığı durumdur.

/// <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
}

Bu kod derler ve Tamam çalışır, ancak Title özelliği paylaşılır.

Açıkçası, Başlık'ın değerinin döndürülmesini, Sınıf 1'i Kitap mı yoksa Kişi mi olarak ele aldığımıza bağlı olarak istiyoruz. Bu, açık arayüzü kullanabileceğimiz zamandır.

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

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

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

Açık arabirim tanımlarının Herkese açık olduğu sonucuna varıldığına dikkat edin ve bu nedenle bunları herkese açık (veya başka şekilde) olarak açıklayamazsınız.

Yine de (yukarıda gösterildiği gibi) bir "paylaşılan" sürüme sahip olabileceğinizi, ancak bu mümkün olsa da, böyle bir mülkün varlığının sorgulanabilir olduğunu unutmayın. Belki de Başlık'ın varsayılan bir uygulaması olarak kullanılabilir - böylece mevcut kodun Class1'i IBook veya IPerson'a yayınlayacak şekilde değiştirilmesi gerekmez.

Eğer "paylaşılan" (örtük) Başlık tanımlamak yoksa, Class1 tüketiciler gerekir aksi kod derlemek olmaz - Açıkça ilk iBook veya IPerson Class1 örneklerini attı.


16

Çoğu zaman açık arabirim uygulaması kullanıyorum. İşte ana nedenleri.

Yeniden düzenleme daha güvenlidir

Bir arayüzü değiştirirken, derleyicinin kontrol edebilmesi daha iyidir. Örtülü uygulamalar için bu daha zordur.

İki yaygın durum akla geliyor:

  • Bir arabirime işlev eklemek, bu arabirimi uygulayan var olan bir sınıfın zaten yenisiyle aynı imzayla bir yöntemi olduğu görülür . Bu beklenmedik davranışlara yol açabilir ve beni birkaç kez sert ısırdı. Hata ayıklama sırasında "görmek" zordur, çünkü bu işlev büyük olasılıkla dosyadaki diğer arayüz yöntemleriyle (aşağıda belirtilen kendi kendini belgeleme sorunu) bulunmaz.

  • Arabirimden bir işlevi kaldırma . Örtülü olarak uygulanan yöntemler aniden ölü kod olacaktır, ancak açıkça uygulanan yöntemler derleme hatasıyla yakalanacaktır. Ölü kod etrafta tutmak iyi olsa bile, onu gözden geçirmek ve tanıtmak zorunda kalmak istiyorum.

C # 'ın bizi bir yöntemi örtük bir uygulama olarak işaretlemeye zorlayan bir anahtar kelimeye sahip olmaması talihsiz bir durumdur, böylece derleyici ekstra kontroller yapabilir. Sanal yöntemlerde, 'geçersiz kılma' ve 'yeni' kullanılması gerektiği için yukarıdaki sorunlardan hiçbiri yoktur.

Not: sabit veya nadiren değişen arabirimler için (genellikle satıcı API'lerinden), bu bir sorun değildir. Kendi arayüzlerim için, ne zaman / nasıl değişeceklerini tahmin edemiyorum.

Kendi kendini belgeliyor

Bir sınıfta 'public bool Execute ()' ifadesini görürsem, bunun bir arayüzün parçası olduğunu anlamak ekstra iş gerektirecektir. Birinin muhtemelen bunu söyleyerek yorum yapması ya da bir bölge ya da "ITask uygulaması" diyen bir grup yorumlaması yapması gerekir. Tabii ki, bu sadece grup başlığı ekran dışında değilse işe yarar ..

Halbuki: 'bool ITask.Execute ()' açık ve nettir.

Arayüz uygulamasının net olarak ayrılması

Arayüzleri, kamusal yöntemlerden daha 'kamusal' olarak düşünüyorum, çünkü bunlar beton tipinin yüzey alanının biraz bir kısmını açığa çıkarmak için üretildi. Türü bir kabiliyete, bir davranışa, bir dizi özelliğe vb. İndirirler. Ve uygulamada, bu ayrılığı sürdürmenin yararlı olduğunu düşünüyorum.

Bir sınıfın koduna baktığımda, açık arayüz uygulamalarına rastladığımda, beynim "kod sözleşmesi" moduna geçer. Genellikle bu uygulamalar sadece diğer yöntemlere yöneliktir, ancak bazen ekstra durum / parametre kontrolü, gelen parametrelerin dahili gereksinimlere daha iyi uyması için dönüşüm veya hatta sürüm oluşturma amaçları için çeviri (örneğin, ortak uygulamalara punting eden birden fazla nesil arayüz) yaparlar.

(Halkın da kod sözleşmesi olduğunu anlıyorum, ancak arayüzler, özellikle beton türlerinin doğrudan kullanımının genellikle sadece dahili kodun bir işareti olduğu arayüz odaklı bir kod tabanında çok daha güçlü.)

İlgili: Jon tarafından yukarıda 2. neden .

Ve bunun gibi

Ayrıca, diğer yanıtlarda daha önce bahsedilen avantajlar:

sorunlar

Hepsi eğlenceli ve mutluluk değil. Bazı etkilere bağlı olduğum bazı durumlar var:

  • Değer türleri, çünkü bu boks ve daha düşük perf gerektirir. Bu katı bir kural değildir ve arayüze ve nasıl kullanılması amaçlandığına bağlıdır. Icomparable? Örtülü. IFormattable? Muhtemelen açık.
  • Sıklıkla doğrudan çağrılan yöntemlere sahip (IDisposable.Dispose gibi) önemsiz sistem arabirimleri.

Ayrıca, aslında beton tipine sahip olduğunuzda ve açık bir arayüz yöntemi çağırmak istediğinizde döküm yapmak bir acı olabilir. Bununla iki yoldan biriyle ilgileniyorum:

  1. Halk ekleyin ve uygulama için arayüz yöntemlerini kendilerine iletin. Genellikle dahili olarak çalışırken daha basit arayüzlerde olur.
  2. (Tercih public IMyInterface I { get { return this; } }ettiğim yöntem) Bir ekleyin (satır içine alınmalıdır) ve arayın foo.I.InterfaceMethod(). Bu yeteneğe ihtiyaç duyan birden fazla arabirim varsa, adı benden öteye genişletin (deneyimime göre bu ihtiyaca sahip olmak nadirdir).

8

Açık bir şekilde uygularsanız, arabirim üyelerine yalnızca arabirim türünde bir başvuru aracılığıyla başvurabilirsiniz. Uygulayıcı sınıfın türü olan bir başvuru, bu arabirim üyelerini göstermez.

Uygulayıcı sınıfınız, sınıfı oluşturmak için kullanılan yöntem (bir fabrika veya IoC olabilir) dışında herkese açık değilse kapsayıcı olabilir) dışında ve arabirim yöntemleri hariç (elbette), o zaman açıkça uygulamak için herhangi bir avantaj görmüyorum arabirimleri.

Aksi takdirde, arayüzleri açıkça uygulamak, somut uygulayıcı sınıfınıza referansların kullanılmamasını sağlar ve bu uygulamayı daha sonra değiştirmenize olanak tanır. "Emin olun", sanırım, "avantaj" dır. İyi faktörlü bir uygulama, bunu açık bir uygulama olmadan gerçekleştirebilir.

Dezavantajı, bence, uygulama kodunda halka açık olmayan üyelere erişimi olan arayüze / arayüzden döküm türlerini bulacağınızdır.

Birçok şey gibi, avantaj dezavantajdır (ve tersi). Arayüzleri açıkça uygulamak, somut sınıf uygulama kodunuzun açıkta kalmamasını sağlayacaktır.


1
Güzel cevap Bill. Diğer cevaplar harikaydı ama kavramamı kolaylaştıran bazı ek objektif bakış açıları (görüşlerinizin üstünde) sağladınız. Çoğu şey gibi, örtülü veya açık uygulamalarla artıları ve eksileri vardır, bu nedenle özel senaryolarınız veya kullanım durumlarınız için en iyisini kullanmanız yeterlidir. Daha iyi anlamaya çalışanların cevabınızı okumaktan fayda sağlayacağını söyleyebilirim.
Daniel Eagle

6

Örtük bir arabirim uygulaması, arabirimin aynı imzasına sahip bir yönteminizin olduğu yerdir.

Açık bir arabirim uygulaması, yöntemin hangi arabirime ait olduğunu açıkça bildirdiğiniz yerdir.

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: örtük ve açık arabirim uygulamaları


6

Bir arabirimi uygulayan her sınıf üyesi, VB.NET arabirimi bildirimlerinin yazılış biçimine semantik olarak benzeyen bir bildirim verir, ör.

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

Sınıf üyesinin adı genellikle arabirim üyesinin adıyla eşleşecek ve sınıf üyesi genellikle herkese açık olacak olsa da, bunların hiçbirine gerek yoktur. Ayrıca beyan edilebilir:

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

Bu durumda, sınıf ve türevlerinin adı kullanarak bir sınıf üyesine erişmesine izin verilir IFoo_Foo, ancak dış dünya yalnızca o üyeye yalnızca döküm yoluyla erişebilir IFoo. Böyle bir yaklaşım, bir arabirim yönteminin tüm uygulamalarda davranış belirlediği durumlarda genellikle iyidir , ancak sadece bazılarında yararlı davranış [örneğin salt okunur bir koleksiyonun IList<T>.Addyöntemi için belirtilen davranış atmaktır NotSupportedException]. Ne yazık ki, arabirimi C # içinde uygulamanın tek uygun yolu:

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

O kadar hoş değil.


2

Açık arabirim uygulamasının önemli bir kullanımı, karışık görünürlüklü arabirimlerin uygulanması gerektiğidir .

Sorun ve çözümü C # Dahili Arayüz makalesinde iyi açıklanmıştır .

Örneğin, uygulama katmanları arasındaki nesnelerin sızmasını korumak istiyorsanız, bu teknik, sızıntıya neden olabilecek üyelerin farklı görünürlüklerini belirtmenize olanak tanır.


2

Önceki cevaplar, C # 'da açıkça bir arabirim uygulamanın neden tercih edilebileceğini açıklamaktadır (çoğunlukla resmi nedenlerle). Bununla birlikte, açık uygulamanın zorunlu olduğu bir durum vardır : Arabirim olmadığında public, ancak uygulayıcı sınıf olduğunda kapsüllemenin sızdırılmasını önlemek için 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() { } }

Yukarıdaki sızıntı kaçınılmazdır, çünkü C # belirtimine göre "Tüm arabirim üyeleri dolaylı olarak genel erişime sahiptir." Sonuç olarak, publicarayüzün kendisi de olsa , örtük uygulamalar da erişim sağlamalıdır internal.

C # 'da örtülü arayüz uygulaması büyük kolaylıktır. Uygulamada, birçok programcı bunu daha fazla dikkate almadan her zaman / her yerde kullanır . Bu, en iyi dağınık tip yüzeylere ve en kötü şekilde sızdırılmış kapsüllemeye yol açar. F # gibi diğer diller buna izin vermiyor .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.