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


223

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


21
インターフェイスには実装がありません。これには、抽象クラスを使用し、プロパティYear
PostMan

6
ここで述べたことに加えて、インターフェイスはコントラクトであり、フィールドは、値(スカラーまたはアドレスポインター)を入れるマシンのメモリ内のスロットを定義するという点で、実装の詳細です。
ヘルツマイスター、2010年

5
しかし、フィールドが公開されている場合、それは契約の一部であり、実装の詳細だけではありませんよね?
アレックス

回答:


238

他の答えの多くはセマンティックレベルでは正しいですが、実装の詳細レベルからこのような種類の質問にも取り組むのは興味深いことです。

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

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メソッドも含めることができます。しかし、フィールドはメソッドではありません。フィールドに関連付けられた「スロット」はなく、フィールドの場所への参照を使用して「入力」できます。したがって、インターフェイスはメソッド、プロパティ、インデクサー、イベントを定義できますが、フィールドは定義できません。


26
私が時々見落としていることの1つは、インターフェースレベルの定数を定義するjavaのような機能です。これはおそらく、言語でのサポートに「スロット」を必要としないでしょう。
LBushkin 2010年

2
簡単な言葉での説明が好きです。ありがとう。詳細については、「C#を介したCLR」および「Essential .netボリューム1」を参照してください。
Sandeep GB、

6
フィールドにスロットがないのはなぜですか?オペレーターと同じ質問ですか?クラスがインターフェイスを継承していなくても、インターフェイスが実装されているかどうかを確認するために、リフレクションを使用したダックタイピングについて聞いた覚えがあります。フィールドを引き上げるために反射(またはスロット)を使用できないのはなぜですか?私はまだコードを書いているので、フィールドが必要ない/必要ないかもしれませんが、演算子を使用できないことに驚きました。演算子は、すべてがオーバーロードされるわけではないことを除いて、私の理解からのメソッドとまったく同じです(msdn.microsoft.com/en-us/library/ms173156.aspxAn interface cannot contain constants, fields, operatorsから)

@acidzombie:インターフェイスが演算子を定義できない理由の問題は、フィールドを含めることができない理由とは(おそらく関連していますが)異なります。それでも興味がある場合は、別の質問を投稿することをお勧めします。
Adam Robinson

1
@ b1nary.atr0phyフィールドを使用する理由 たとえば、メソッドを宣言した場合int One()、実装public int One(){return 1;}はフィールドではありません。
Hi-Angel

131

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

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

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

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

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

5
契約の一部が公開されているすべてではありません。クラスにpublic int Yearがある場合、クラスコントラクトにはYearタイプのフィールドがあり、それにアクセス可能であると言っていませんか?
Didier A. 14年

1
パーティーには遅れますが、この場合は違います。これは、契約に、任意の従属クラスが実装することになっているプロパティ年があることを意味します。プロパティは実際にはget / setメソッドであり、特別なロジックが不要な場合にバッキングフィールドが自動的に生成されます。特別な構文は、より明確な表記のためのものです。
user3613916 14年

その自動実装に定数デフォルト値(123など)を定義するにはどうすればよいYearですか?
lama12345 2017

1
@ lama12345パーティーにも遅れますが、C#6(2015、.NET Framework 4.6および.NET Core)以降、その目的で自動プロパティを使用できます。public int Year => 123;。しかし、この場合には、インターフェイスがで定義されなければならないので、セッターを持ってしても意味がありませんint Year { get; }
EriF89

56

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

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

47
問題は、「C#インターフェイスにフィールドを含めることができないのはなぜですか?」です。これでは対処できません。
AakashM、2010年

14
この答えはOPの質問には答えませんが、問題は解決しました。
Gorgen

36

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

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


エリックが言及したスロットの概念に技術/実際の名前(v-table)を提供しました。詳細はハンスありがとうございます。
RBT 2017

インターフェイスがデフォルトの実装を許可されていない場合、v-tableのポイントは何でしょうか?これはC#8.0で変更されましたが、それは重要ではありません。
AnthonyMonterrosa

19

Year完全に素晴らしいプロパティを持たないのはなぜですか?

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

例えば、あなたの一部Year仕様がために、それが無効であることを必要とするかもしれないICar実装がに割り当てできるようにYear、後に現在の年よりも+言うこと方法はありません1または1900年前に、あなたが露出していた場合Yearフィールド-はるかに優れて使用します代わりにここで作業を行うためのプロパティ。


18

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

あなたが提案することを使用してこのシナリオを考えてください:

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

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

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

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

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

public abstract class MyInterfaceBase : IMyInterface
{
    int myProperty;

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

7

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

public interface IView {
    Control Year { get; }
}


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

3

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

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

3

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

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


1
メンバーにアクセスする必要がある場合は、それをプロパティにしてください。
Unome 2016

0

このため、年フィールドを実装するCar基本クラスを作成でき、他のすべての実装はそれから継承できます。


0

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.