web-dev-qa-db-ja.com

C#インターフェイスにフィールドを含めることができないのはなぜですか?

たとえば、ICarインターフェイスが必要で、すべての実装にフィールドYearが含まれるとします。これは、すべての実装が個別にYearを宣言する必要があるということですか?インターフェースでこれを単純に定義する方が良いと思いませんか?

207
deltanovember

他の回答の多くはセマンティックレベルで正しいものですが、実装の詳細レベルからこの種の質問にアプローチすることも興味深いと思います。

インターフェイスは、slotsのコレクションと考えることができます。これには、methods。クラスがインターフェースを実装する場合、クラスは、必要なすべてのスロットを埋める方法をランタイムに伝える必要があります。あなたが言う時

interface IFoo { void M(); } 
class Foo : IFoo { public void M() { ... } }

クラスは「あなたが私のインスタンスを作成するとき、IFoo.MのスロットにFoo.Mへの参照を詰めます。

次に、電話をかけると:

IFoo ifoo = new Foo();
ifoo.M();

コンパイラは、「オブジェクトにIFoo.Mのスロットにあるメソッドを尋ね、そのメソッドを呼び出す」というコードを生成します。

インターフェイスがメソッドを含むスロットのコレクションである場合、これらのスロットの一部には、プロパティのgetおよびsetメソッド、インデクサーのgetおよびsetメソッド、イベントのaddおよびremoveメソッドも含めることができます。しかし、フィールドはメソッドではありません。フィールドに関連付けられた「スロット」はなく、フィールドの場所への参照を「入力」できます。したがって、インターフェイスはメソッド、プロパティ、インデクサー、イベントを定義できますが、フィールドは定義できません。

226
Eric Lippert

C#のインターフェイスは、特定の実装ではなく、クラスが準拠するコントラクトを定義することを目的としています。

その精神では、C#インターフェースは定義されるallowプロパティーを許可します-呼び出し側は以下の実装を提供する必要があります:

interface ICar
{
    int Year { get; set; }
}

プロパティに関連付けられた特別なロジックがない場合、クラスの実装は自動プロパティを使用して実装を簡素化できます。

class Automobile : ICar
{
    public int Year { get; set; } // automatically implemented
}
126
LBushkin

プロパティとして宣言します。

interface ICar {
   int Year { get; set; }
}
48
Tarydon

Eric Lippertがそれを打ち付けました。彼の言ったことを別の方法で言います。インターフェイスのすべてのメンバーは仮想であり、すべてインターフェイスを継承するクラスによってオーバーライドする必要があります。インターフェイス宣言で仮想キーワードを明示的に記述したり、クラスでオーバーライドキーワードを使用したりしないでください。

仮想キーワードは、.NETでメソッドと、いわゆるvテーブル(メソッドポインターの配列)を使用して実装されます。 overrideキーワードは、v-tableスロットを別のメソッドポインターで埋め、基本クラスによって生成されたものを上書きします。プロパティ、イベント、およびインデクサーは、内部でメソッドとして実装されます。しかし、フィールドはそうではありません。したがって、インターフェイスにフィールドを含めることはできません。

35
Hans Passant

なぜYearプロパティを持っているだけでなく、これはまったく問題ありませんか?

フィールドはデータ表現の特定の実装を表し、それらを公開するとカプセル化が解除されるため、インターフェイスにはフィールドが含まれません。したがって、フィールドを持つインターフェースを持つことは、インターフェースの代わりに実装に効果的にコーディングすることになります。これは、インターフェースが持つ奇妙な矛盾です!

たとえば、Year仕様の一部では、ICar実装者が現在の年+ 1以降または1900年より前のYearへの割り当てを許可する必要がある場合があります。 Yearフィールドを公開している場合は、ここで作業を行う代わりにプロパティを使用する方がはるかに優れていると言えます。

19
John Feminella

簡単な答えはイエスです。すべての実装タイプは独自のバッキング変数を作成する必要があります。これは、インターフェイスがコントラクトに類似しているためです。できるのは、実装する型が利用可能にしなければならない特定の公開アクセス可能なコードを指定することだけです。コード自体を含めることはできません。

あなたが提案するものを使用して、このシナリオを検討してください。

public interface InterfaceOne
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public interface InterfaceTwo
{
    int myBackingVariable;

    int MyProperty { get { return myBackingVariable; } }
}

public class MyClass : InterfaceOne, InterfaceTwo { }

ここにはいくつかの問題があります。

  • インターフェイスのすべてのメンバーは(定義により)publicであるため、バッキング変数はインターフェイスを使用するすべてのユーザーに公開されるようになりました
  • どのmyBackingVariableMyClassを使用しますか?

最も一般的なアプローチは、インターフェイスと、それを実装する最低限の抽象クラスを宣言することです。これにより、抽象クラスから継承して実装を無料で取得するか、インターフェイスを明示的に実装して別のクラスから継承することを許可するという柔軟性が得られます。次のように機能します。

public interface IMyInterface
{
    int MyProperty { get; set; }
}

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

    public int MyProperty
    {
        get { return myProperty; }
        set { myProperty = value; }
    }
}
17
Adam Robinson

他の人が「なぜ」を与えているので、あなたのインターフェースがコントロールを定義できることを付け加えます。プロパティでラップする場合:

public interface IView {
    Control Year { get; }
}


public Form : IView {
    public Control Year { get { return uxYear; } } //numeric text box or whatever
}
6
zomf

インターフェイスには実装が含まれていません。

  1. プロパティを使用してインターフェイスを定義します。
  2. さらに、任意のクラスにそのインターフェイスを実装し、今後このクラスを使用できます。
  3. 必要に応じて、このプロパティをクラスで仮想として定義して、その動作を変更できます。
3
Amit

すでに多くのことが言われていますが、簡単にするために、ここに私の見解を示します。インターフェイスは、コンシューマまたはクラスによって実装されるメソッドコントラクトを持ち、値を格納するフィールドを持たないことを目的としています。

あなたは、なぜプロパティが許可されるのかと主張するかもしれません。簡単な答えは、プロパティはメソッドとしてのみ内部的に定義されているということです。

2

インターフェイスはpublicインスタンスのプロパティとメソッドを定義します。通常、フィールドはプライベートであるか、最大限に保護された内部、または保護された内部です(「フィールド」という用語は、一般にパブリックには使用されません)。

他の返信で述べられているように、基本クラスを定義し、すべての継承者がアクセスできる保護されたプロパティを定義できます。

奇妙な点の1つは、インターフェイスを実際にinternalとして定義できるが、インターフェイスの有用性を制限することであり、通常は他の外部コードで使用されない内部機能を定義するために使用されます。

0
Frode N. Rosand

このために、yearフィールドを実装するCar基本クラスを持つことができ、他のすべての実装はそれから継承できます。

0
IK