web-dev-qa-db-ja.com

C#インターフェイスメソッドが抽象または仮想として宣言されていないのはなぜですか?

インターフェイスのC#メソッドは、virtualキーワードを使用せずに宣言され、overrideキーワードを使用せずに派生クラスでオーバーライドされます。

これには理由がありますか?私はそれが単なる言語の利便性であり、明らかにCLRはこれを内部で処理する方法を知っていると思います(メソッドはデフォルトでは仮想ではありません)が、他の技術的な理由はありますか?

派生クラスが生成するILは次のとおりです。

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

メソッドがILでvirtualfinalとして宣言されていることに注意してください。

102
Robert Harvey

インターフェースの場合、abstract、またはpublicキーワードの追加は冗長になるため、それらを省略します。

interface MyInterface {
  void Method();
}

CILでは、メソッドはvirtualおよびabstractとマークされます。

(Javaを使用すると、インターフェイスメンバーをpublic abstractとして宣言できます)。

実装クラスには、いくつかのオプションがあります。

オーバーライド不可:C#では、クラスはメソッドをvirtualとして宣言しません。つまり、派生クラスでオーバーライドすることはできません(非表示のみ)。 CILでは、メソッドはインターフェイスタイプに関するポリモーフィズムをサポートする必要があるため、依然として仮想(ただし封印済み)です。

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

オーバーライド可能:C#とCILの両方で、メソッドはvirtualです。ポリモーフィックディスパッチに参加し、オーバーライドできます。

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

Explicit:これは、クラスがインターフェイスを実装する方法ですが、クラス自体のパブリックインターフェイスにインターフェイスメソッドを提供しません。 CILでは、メソッドはprivate(!)になりますが、対応するインターフェイスタイプへの参照からクラスの外部から呼び出し可能です。明示的な実装もオーバーライドできません。これは、プライベートメソッドを実装している対応するインターフェイスメソッドにリンクするCILディレクティブ(.override)があるため可能です。

[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では、実装クラスのインターフェイスメソッド名をエイリアスすることもできます。

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

さて、この奇妙なケースを考えてみましょう:

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

BaseDerivedが同じアセンブリで宣言されている場合、コンパイラは、Baseがそうでない場合でも、Base::Methodを仮想化し、シール(CIL内)します。インターフェイスを実装します。

BaseDerivedが異なるアセンブリにある場合、Derivedアセンブリをコンパイルするとき、コンパイラは他のアセンブリを変更しないため、Derivedは、MyInterface::Methodの明示的な実装であり、Base::Methodへの呼び出しを委任するだけです。

つまり、everyインターフェイスメソッドの実装は多態的な動作をサポートする必要があるため、コンパイラがやれ。

140
Jordão

Charp 3rd Editionを介してCLRからJeffrey Ritcherを引用する

CLRでは、インターフェイスメソッドを仮想としてマークする必要があります。ソースコードでメソッドを明示的に仮想としてマークしない場合、コンパイラはメソッドを仮想および封印済みとしてマークします。これにより、派生クラスがインターフェイスメソッドをオーバーライドできなくなります。メソッドを明示的に仮想としてマークすると、コンパイラーはメソッドを仮想としてマークします(そして、そのメソッドは封印されません)。これにより、派生クラスがインターフェイスメソッドをオーバーライドできます。インターフェイスメソッドがシールされている場合、派生クラスはメソッドをオーバーライドできません。ただし、派生クラスは同じインターフェイスを再継承し、インターフェイスのメソッドに独自の実装を提供できます。

72
Usman Khan

はい、ランタイムに関する限り、インターフェイス実装メソッドは仮想です。これは実装の詳細であり、インターフェースを機能させます。仮想メソッドはクラスのvテーブルにスロットを取得し、各スロットには仮想メソッドの1つへのポインターがあります。オブジェクトをインターフェイス型にキャストすると、インターフェイスメソッドを実装するテーブルのセクションへのポインタが生成されます。インターフェイス参照を使用するクライアントコードは、インターフェイスポインターなどからオフセット0にある最初のインターフェイスメソッドポインターを参照するようになりました。

元の答えで過小評価していたのは、final属性の重要性です。派生クラスが仮想メソッドをオーバーライドするのを防ぎます。派生クラスは、インターフェイス、実装メソッドを再実装する必要がありますshadow基本クラスメソッド。実装方法は仮想ではないというC#言語コントラクトを実装するにはこれで十分です。

ExampleクラスのDispose()メソッドをvirtualとして宣言すると、final属性が削除されます。派生クラスがそれをオーバーライドできるようになりました。

12
Hans Passant

他のほとんどのコンパイル済みコード環境では、インターフェイスはvtable-メソッド本体へのポインタのリストとして実装されます。通常、複数のインターフェースを実装するクラスは、内部コンパイラーで生成されたメタデータのどこかに、インターフェースvtableのリスト(インターフェースごとに1つのvtable)を持っています(メソッドの順序は保持されます)。これは通常、COMインターフェイスも同様に実装される方法です。

ただし、.NETでは、インターフェイスは各クラスの個別のvtableとして実装されていません。インターフェイスメソッドは、すべてのインターフェイスが属するグローバルインターフェイスメソッドテーブルを通じてインデックスが作成されます。したがって、メソッドがインターフェイスメソッドを実装するためにメソッドを仮想的に宣言する必要はありません。グローバルインターフェイスメソッドテーブルは、クラスのメソッドのコードアドレスを直接指すことができます。

インターフェイスを実装するために仮想メソッドを宣言することは、非CLRプラットフォームであっても、他の言語でも必要ありません。 Win32のDelphi言語はその一例です。

4
dthorpe

それらは仮想ではありません(それらをどのように考えるかについては、(実装された仮想)としての基礎的な実装の観点からではありません-ここで他の答えを読んで自分で何かを学ぶのは良いです:-)

それらは何もオーバーライドしません-インターフェイスに実装はありません。

インターフェースが行うことは、クラスが従わなければならない「契約」を提供することです-必要に応じて、特定のクラスを見たことがない場合でも呼び出し元がオブジェクトを呼び出す方法を知るためのパターン。

インターフェースのメソッドを実装するのはクラス次第です。これは、コントラクトの範囲内で、仮想または「非仮想」(最終的には密閉された仮想)です。

0
Jason Williams

<joke>これは、Anders Hejlsbergおよびその他のC#設計チームに尋ねたいものです。</ joke>

インターフェースはクラスよりも抽象的な概念です。インターフェースを実装するクラスを宣言するとき、「クラスはインターフェースからこれらの特定のメソッドを持たなければならず、静的virtualnon virtualoverriden、同じIDと同じ型パラメーターを持っている限り」.

Object Pascal( "Delphi")やObjective-C(Mac)などのインターフェイスをサポートする他の言語では、インターフェイスメソッドを仮想ではなく仮想としてマークする必要もありません。

しかし、あなたは正しいかもしれませんが、特定のインターフェイスを実装するクラスメソッドをrestrict 。しかし、それはまた、両方のインターフェースに「非仮想」、「dontcareifvirtualornot」キーワードを持つことを意味します。

クラスメソッドが「@virtual」または「@override」を使用してメソッドが仮想であることを確認する必要がある場合、Javaでも似たようなものが表示されるため、あなたの質問を理解しています。

0
umlcat