C # arabirim yöntemleri neden soyut veya sanal olarak bildirilmiyor?


108

Arabirimlerdeki C # yöntemleri virtualanahtar sözcük kullanılmadan bildirilir ve anahtar sözcük kullanılmadan türetilmiş sınıfta geçersiz kılınır override.

Bunun bir sebebi var mı? Bunun sadece bir dil kolaylığı olduğunu varsayıyorum ve CLR bunu kapaklar altında nasıl ele alacağını biliyor (yöntemler varsayılan olarak sanal değildir), ancak başka teknik nedenler var mı?

Türetilmiş bir sınıfın oluşturduğu IL burada:

class Example : IDisposable {
    public void Dispose() { }
}

.method public hidebysig newslot virtual final 
        instance void  Dispose() cil managed
{
  // Code size       2 (0x2)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ret
} // end of method Example::Dispose

Yöntemin virtual finalIL'de bildirildiğine dikkat edin .

Yanıtlar:


145

Arayüz için abstract, publicanahtar kelimelerin eklenmesi veya hatta anahtar kelimelerin eklenmesi gereksiz olacaktır, bu nedenle onları atlarsınız:

interface MyInterface {
  void Method();
}

CIL'de yöntem işaretlenir virtualve abstract.

(Java'nın arabirim üyelerinin bildirilmesine izin verdiğini unutmayın public abstract).

Uygulama sınıfı için bazı seçenekler vardır:

Geçersiz kılınamaz : C # 'da sınıf yöntemi olarak bildirmez virtual. Bu, türetilmiş bir sınıfta geçersiz kılınamayacağı anlamına gelir (yalnızca gizli). CIL'de yöntem hala sanaldır (ancak mühürlenmiştir) çünkü arayüz tipine ilişkin polimorfizmi desteklemesi gerekir.

class MyClass : MyInterface {
  public void Method() {}
}

Geçersiz kılınabilir : Hem C # hem de CIL'de yöntem virtual. Polimorfik gönderime katılır ve geçersiz kılınabilir.

class MyClass : MyInterface {
  public virtual void Method() {}
}

Açık : Bu, bir sınıfın bir arabirim uygulaması için bir yoldur, ancak sınıfın kendi genel arabiriminde arabirim yöntemlerini sağlamaz. CIL'de yöntem private(!) Olacaktır, ancak yine de sınıfın dışından karşılık gelen arayüz türüne bir referanstan çağrılabilir olacaktır. Açık uygulamalar da geçersiz kılınamaz. Bu mümkündür, çünkü .overrideözel yöntemi uyguladığı ilgili arabirim yöntemine bağlayacak bir CIL yönergesi ( ) vardır.

[C #]

class MyClass : MyInterface {
  void MyInterface.Method() {}
}

[CIL]

.method private hidebysig newslot virtual final instance void MyInterface.Method() cil managed
{
  .override MyInterface::Method
}

VB.NET'te, uygulama sınıfındaki arabirim yöntemi adını bile değiştirebilirsiniz.

[VB.NET]

Public Class MyClass
  Implements MyInterface
  Public Sub AliasedMethod() Implements MyInterface.Method
  End Sub
End Class

[CIL]

.method public newslot virtual final instance void AliasedMethod() cil managed
{
  .override MyInterface::Method
}

Şimdi, şu garip durumu bir düşünün:

interface MyInterface {
  void Method();
}
class Base {
  public void Method();
}
class Derived : Base, MyInterface { }

Eğer Baseve Derivedaynı derlemede ilan edilir, derleyici yapacak Base::Methodolsa, sanal ve (CIL olarak) mühürlü Basearabirimini uygulamaz.

Eğer Baseve Derivedfarklı derlemeler vardır derlerken, Derivedmontaj bunun bir üyesini bu yüzden tanıtacağım derleyici, diğer montaj değişmeyecek Derivediçin açık bir uygulama olacağı MyInterface::Methodbu sadece çağrısı devredeceğini Base::Method.

Gördüğünüz gibi, her arabirim yöntemi uygulaması polimorfik davranışı desteklemelidir ve bu nedenle, derleyicinin bunu yapmak için çemberlerden geçmesi gerekse bile CIL'de sanal olarak işaretlenmelidir.


73

Jeffrey Ritcher'dan CLR'den CSharp 3rd Edition aracılığıyla alıntı burada

CLR, arayüz yöntemlerinin sanal olarak işaretlenmesini gerektirir. Yöntemi kaynak kodunuzda açık bir şekilde sanal olarak işaretlemezseniz, derleyici yöntemi sanal ve mühürlenmiş olarak işaretler; bu, türetilmiş bir sınıfın arabirim yöntemini geçersiz kılmasını önler. Yöntemi açık bir şekilde sanal olarak işaretlerseniz, derleyici yöntemi sanal olarak işaretler (ve mühürsüz bırakır); bu, türetilmiş bir sınıfın arabirim yöntemini geçersiz kılmasına izin verir. Bir arabirim yöntemi mühürlenirse, türetilmiş bir sınıf yöntemi geçersiz kılamaz. Bununla birlikte, türetilmiş bir sınıf aynı arabirimi yeniden devralabilir ve arabirimin yöntemleri için kendi uygulamasını sağlayabilir.


25
Alıntı, neden bir arayüz yöntemi uygulamasının sanal olarak işaretlenmesi gerektiğini söylemiyor . Bunun nedeni, arayüz türüne göre polimorfik olmasıdır, bu nedenle sanal yöntem dağıtımına izin vermek için sanal tabloda bir yuvaya ihtiyaç duyar .
Jordão

1
Arabirim yöntemini açık bir şekilde sanal olarak işaretlememe ve "CS0106 hatası:" sanal "değiştiricisi bu öğe için geçerli değil" hatası almama izin verilmiyor. V2.0.50727 (bilgisayarımdaki en eski sürüm) kullanılarak test edildi.
ccppjava

3
@ccppjava Jorado'nun aşağıdaki yorumundan, alt sınıfların sınıfı geçersiz kılmasına izin vermek için sanal arabirimi uygulayan sınıf üyesini işaretlersiniz.
Christopher Stevenson

13

Evet, arayüz uygulama yöntemleri, çalışma zamanı söz konusu olduğunda sanaldır. Bir uygulama detayıdır, arayüzlerin çalışmasını sağlar. Sanal yöntemler sınıfın v tablosundaki yuvaları alır, her yuvanın sanal yöntemlerden birine bir işaretçisi vardır. Bir nesnenin bir arabirim türüne atanması, arabirim yöntemlerini uygulayan tablonun bölümüne bir işaretçi oluşturur. Arayüz referansını kullanan istemci kodu artık ilk arayüz metodu işaretçisini arayüz işaretçisinden, vb.'den ofset 0'da görür.

Orijinal cevabımda az takdir ettiğim şey, son niteliğin önemi . Türetilmiş bir sınıfın sanal yöntemi geçersiz kılmasını önler. Türetilmiş bir sınıf arabirimi yeniden uygulamalıdır, uygulama yöntemleri temel sınıf yöntemlerini gölgeler . Bu, uygulama yönteminin sanal olmadığını söyleyen C # dil sözleşmesini uygulamak için yeterlidir.

Example sınıfında Dispose () yöntemini sanal olarak bildirirseniz, son niteliğin kaldırıldığını görürsünüz . Şimdi türetilmiş bir sınıfın onu geçersiz kılmasına izin verilir.


4

Diğer derlenmiş kod ortamlarının çoğunda, arabirimler vtables olarak uygulanır - yöntem gövdelerine işaretçilerin bir listesi. Tipik olarak, birden çok arabirim uygulayan bir sınıf, dahili derleyicinin ürettiği meta verilerde bir arabirim vtables listesine sahip olacaktır, arabirim başına bir vtable (böylece yöntem sırası korunur). COM arabirimleri de bu şekilde uygulanır.

Ancak .NET'te, arabirimler her sınıf için ayrı sanal tablolar olarak uygulanmaz. Arabirim yöntemleri, tüm arabirimlerin bir parçası olduğu küresel arabirim yöntemi tablosu aracılığıyla dizinlenir. Bu nedenle, bu yöntemin bir arabirim yöntemini gerçekleştirmesi için bir yöntemi sanal olarak bildirmek gerekli değildir - genel arabirim yöntemi tablosu yalnızca sınıfın yönteminin kod adresini doğrudan gösterebilir.

Bir arabirimi uygulamak için bir yöntemi sanal olarak bildirmek, CLR olmayan platformlarda bile diğer dillerde de gerekli değildir. Win32'deki Delphi dili bir örnektir.


0

Sanal değiller (onları nasıl düşündüğümüz açısından, temelde yatan uygulama açısından değilse (kapalı sanal) - buradaki diğer yanıtları okumak ve kendim bir şeyler öğrenmek güzel :-)

Hiçbir şeyi geçersiz kılmazlar - arayüzde uygulama yoktur.

Arayüzün yaptığı tek şey, sınıfın uymak zorunda olduğu bir "sözleşme" sağlamaktır - isterseniz bir model, böylece arayanlar, belirli bir sınıfı daha önce hiç görmemiş olsalar bile nesneyi nasıl çağıracaklarını bilirler.

Arabirim yöntemini sözleşmenin sınırları dahilinde - sanal veya "sanal olmayan" (ortaya çıktıkça mühürlü sanal) uygulamak sınıfa kalmıştır.


bu ileti dizisindeki herkes arabirimlerin ne işe yaradığını bilir. Sorunu, çok özeldir - üretilen IL olan arayüz yöntemi için sanal olmayan bir arayüz yöntemi için sanal değil.
Rex M

5
Evet, soru düzenlendikten sonra bir cevabı eleştirmek gerçekten çok kolay, değil mi?
Jason Williams

0

Arabirimler, sınıflardan daha soyut bir kavramdır, bir arabirimi uygulayan bir sınıf bildirdiğinizde, "sınıfın arabirimden bu belirli yöntemlere sahip olması gerekir ve statik , sanal , sanal olmayan , geçersiz kılınması fark etmez" diyorsunuz . aynı ID ve aynı tip parametrelere sahip olduğu sürece ".

Object Pascal ("Delphi") ve Objective-C (Mac) gibi arabirimleri destekleyen diğer diller de arabirim yöntemlerinin sanal değil sanal olarak işaretlenmesini gerektirmez.

Ancak, haklı olabilirsiniz, belirli bir arabirimi uygulayan sınıf yöntemlerini kısıtlamak istemeniz durumunda, arabirimlerde belirli bir "sanal" / "geçersiz kılma" özniteliğine sahip olmanın iyi bir fikir olabileceğini düşünüyorum . Ancak bu, her iki arayüz için de "sanal olmayan", "dontcareifvirtualornot" anahtar kelimelerine sahip olmak anlamına gelir.

Sorunuzu anlıyorum, çünkü bir yöntemin sanal olmasını sağlamak için bir sınıf yönteminin "@virtual" veya "@override" kullanması gerektiğinde Java'da benzer bir şey görüyorum.


@override aslında kodun davranışını değiştirmez veya ortaya çıkan bayt kodunu değiştirmez. Yaptığı şey, derleyiciye bu şekilde dekore edilmiş yöntemin geçersiz kılma amacını taşıdığına dair sinyal vermektir, bu da derleyicinin bazı akıl sağlığı kontrolleri yapmasına izin verir. C # farklı çalışır; overridedilin kendisinde birinci sınıf bir anahtar kelimedir.
Robert Harvey
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.