60

私のプロジェクトでは、コンパイル時のエラーがないため、C# で完全に有効と思われる奇妙な状況を見つけました。

簡単な例は次のようになります。

using System;
using System.Collections.Generic;

namespace Test
{

    interface IFoo
    {
        void FooMethod();
    }

    class A
    {
        public void FooMethod()
        {
            Console.WriteLine("implementation");
        }
    }

    class B : A, IFoo
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            IFoo foo = new B();
            foo.FooMethod();
        }
    }
}

そのようなコードはコンパイルされます。ただし、メソッドは実装されておらず、実装されていないAことに注意してください。私の場合、偶然(リファクタリング後)、同じシグネチャを持つメソッドがあります。しかし、なぜインターフェイスの実装方法を知っている必要があるのでしょうか? それが存在することさえ知りません。IFooBIFooAAFooMethodIFooAIFoo

私にとって、そのようなデザインを持つことは危険です。インターフェイスを実装するたびに、このインターフェイスの各メソッドが基本クラスのメソッドと「干渉」するかどうかを確認する必要があるためです。

これが「純粋な C# 機能」の場合は? それはなんと呼ばれていますか?何か不足していますか?

4

10 に答える 10

42

インターフェイスの各メンバーに対して、コンパイラは単純に明示的な実装 (存在する場合) を探し、次にパブリック実装 (暗黙的な実装)、つまりインターフェイス シグネチャに一致するパブリック API のメソッドを探します。この場合、A.FooMethod()公開実装にぴったりのように見えます。Bその選択に満足できない場合はnew、メソッドを使用するか、明示的な実装を使用できます。後者が優先されます。

void IFoo.FooMethod() { /* explicit implementation */ }
于 2013-02-05T09:20:31.197 に答える
20

ここでのキーワードはimplements. IFoo基本クラスは、クラス階層のどこかでインターフェイスにメソッドを実装するメソッド シグネチャが宣言されていることについては何も知りません。

したがってIFoo、派生クラスに実装すると、クラス構造内に実装されたメソッド シグネチャが既にあるため、再度実装する必要はありません。

あなたがこれを持っていたら:

interface IFoo
{
  void FooMethod();
}
class A
{
  private void FooMethod(){}
}
class B : A, IFoo
{

}

マークが言うように、実装された時点では構造にアクセスできないIFooため、この場合は実装する必要があります。階層内で適切なメソッド シグネチャが既に定義されているにもかかわらず、確実に実装できるようにすることでIFoo、インターフェイスを暗黙的に実装できます。IFoo.FooMethod()

于 2013-02-05T09:19:30.583 に答える
12

あなたはコメントで、

実装しないFooMethodinクラスの実装を書いた人が実際に実装するつもりだった可能性はどれくらいありますか?AIFooIFoo

まあ、作者の創作A時の思想はどうでもいい。both がAND implementsから継承されているという事実に対して責任を負わなければならないAのは、 の作成者です。の定義の結果について考えるのは、 の作成者次第です。BBAIFooBB

あなたも言う

私の場合、偶然(リファクタリング後)Aに同じシグネチャのメソッドがあります

Aこの状況は後に発生し、B両方が書かれたことを示唆しています。Aその場合、状況が変わります: ( などの)から *継承された * クラスを編集する場合、すべての継承クラスに対する編集の影響を確認するのは編集者の責任です。

于 2013-02-05T13:39:38.703 に答える
2

インターフェイスを実装するには、クラスは (a) そのインターフェイスを実装していることを宣言し (クラス B が行うように)、(b) インターフェイスで定義されたすべてのメソッドの実装を、直接的または間接的に基本クラス(クラス B など)。

于 2013-02-05T09:20:28.393 に答える
2

セクション 13.4.4。C# 仕様の状態:

クラスまたは構造体 C のインターフェイス マッピングは、C の基本クラス リストで指定された各インターフェイスの各メンバーの実装を検索します。特定のインターフェイス メンバー IM の実装が決定されます。ここで、I はメンバー M が宣言されているインターフェイスです。各クラスまたは構造体 S を調べ、C から始めて、一致するものが見つかるまで、C の連続する各基本クラスに対して繰り返します。

FooMethodの正しい実装が に見つからないため、これは明確に定義された動作であると思われます。したがって、署名が一致するメソッドが見つかったB基本クラスで検索が実行されます。Aこれは、仕様の同じセクションでも明示的に指摘されています。

基本クラスのメンバーは、インターフェイス マッピングに参加します。例では

interface Interface1
{
void F();
}
class Class1
{
    public void F() {}
    public void G() {}
}
class Class2: Class1, Interface1
{
    new public void G() {}
}

Class1 のメソッド F は、Class2 の Interface1 の実装で使用されます。

于 2013-02-05T12:26:18.233 に答える
1

インターフェイスは継承されず、インターフェイスが実装されます。したがって、インターフェイスからクラスを派生させる場合、それは次のことを意味します。

ちょっとインターフェース、あなたはあなたが提供したメソッド署名を実装するメソッドをここに見つけるでしょう。

基本クラスにはメソッドの実装があり、同じメソッド署名がインターフェースで定義されているため、問題はありません。

同じメソッド署名を含む2番目のインターフェースを作成しても、それは機能します。

interface IFoo2
{
    void FooMethod();
}

class B : A, IFoo, IFoo2
{
}
于 2013-02-05T09:23:29.547 に答える
1

「しかし、なぜ A は IFoo インターフェイスの FooMethod を実装する方法を知っている必要があるのでしょうか? A でさえ、IFoo が存在することを知りません。」

A はインターフェイス IFoo の存在を知る必要はありません。FooMethod を正しく実装するのは A の責任ではありません。どうやら A はたまたま IFoo インターフェイス メソッド FooMethod と同じシグネチャを持つメソッドを実装していたようです。

IFoo インターフェイスを実装しているため、FooMethod を実装するのは B の責任です。しかし、B には既に FooMethod という名前のメソッド (A から継承) があるため、明示的に実装する必要はありません。継承されたメソッドが機能していない場合、B はメソッドを新規作成し、独自の実装を作成できます。

于 2013-02-06T03:48:57.103 に答える
1

この機能は継承と呼ばれます。また、デザインが気に入らない場合は、使用しないでください。多くの人が継承を嫌います。継承の定義は、基本クラスのすべてのメンバーが派生クラスのメンバーでもあるということです。したがって、コンパイルエラーはありません。したがって、Derived はコントラクトIFooが提供するものを実装します。この要件を満たすのは基本クラスのメンバーです。

その利点は、基本機能(仮想)を介してインターフェイスを実装できることです。これは、Derived が異なる動作をすると予想される場合にオーバーライドできます。

于 2013-02-05T09:22:59.407 に答える
0

C# の作成者がそのようなことをした理由を推測することは特に役に立ちませんが、私はこの特定の機能が好きではありませんが、C# が機能する理由の一部は、に適切な構文がないためだと思います。インターフェイスが既存の基本クラス メソッドによって実装される必要があることを指定します。派生クラスが、基本クラスの実装に連鎖するだけのメソッドを定義しなければならないと要求するのは見苦しく思えます。

そうは言っても、自動バインディングを使用するよりも、インターフェースメンバーをクラスメンバーに明示的にアタッチする構文を提供することにより、C#がその一般的な問題(他のメンバーにチェーンするインターフェースメソッドは醜い)を解決する方がクリーンだったと思います1 つの特定の状況を処理するが、より一般的な状況では連鎖が必要なセマンティクス (保護された仮想メソッドによるインターフェイスの実装):

protected virtual IFoo_Method(int a, int b, int c) { ... }

IFoo.Method(int a, int b, int c) { IFoo_Method(a,b,c); }

JITter は、IFoo_Method 呼び出しをインライン化する必要があることを判断できる場合がありますが、実際にはインライン化する必要はありません。IFoo_Method保護されたメソッドが の実装とみなされるべきであると宣言する方がきれいに思えIFoo.Methodます。

于 2013-02-05T21:28:42.243 に答える
0

B実施しておりますIFOOBから継承しAているため、実際には次のようになります。

class B : IFoo  //Notice there is no A here. 
{
    public void FooMethod()
    {
        Console.WriteLine("implementation");
    }
}

Bそして、(上記のコードから) を実装していることは明らかであり、IFoo特別なことは何もありません。

于 2013-02-05T09:31:17.843 に答える