たとえば、ICar
インターフェイスが必要で、すべての実装にフィールドが含まれているとしますYear
。これは、すべての実装が個別に宣言する必要があることを意味しYear
ますか?これをインターフェースで単純に定義した方がいいのではないでしょうか?
たとえば、ICar
インターフェイスが必要で、すべての実装にフィールドが含まれているとしますYear
。これは、すべての実装が個別に宣言する必要があることを意味しYear
ますか?これをインターフェースで単純に定義した方がいいのではないでしょうか?
回答:
他の答えの多くはセマンティックレベルでは正しいですが、実装の詳細レベルからこのような種類の質問にも取り組むのは興味深いことです。
インターフェースは、メソッドを含むスロットのコレクションと考えることができます。クラスがインターフェースを実装するとき、クラスはランタイムに必要なすべてのスロットを埋める方法を伝える必要があります。あなたが言う時
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メソッドも含めることができます。しかし、フィールドはメソッドではありません。フィールドに関連付けられた「スロット」はなく、フィールドの場所への参照を使用して「入力」できます。したがって、インターフェイスはメソッド、プロパティ、インデクサー、イベントを定義できますが、フィールドは定義できません。
An interface cannot contain constants, fields, operators
から)
int One()
、実装public int One(){return 1;}
はフィールドではありません。
C#のインターフェイスは、特定の実装ではなく、クラスが準拠するコントラクトを定義することを目的としています。
その精神で、C#インターフェイスはプロパティを定義することを許可します-呼び出し元は次の実装を提供する必要があります:
interface ICar
{
int Year { get; set; }
}
プロパティに関連付けられた特別なロジックがない場合、実装クラスは自動プロパティを使用して実装を簡略化できます。
class Automobile : ICar
{
public int Year { get; set; } // automatically implemented
}
Year
ですか?
public int Year => 123;
。しかし、この場合には、インターフェイスがで定義されなければならないので、セッターを持ってしても意味がありませんint Year { get; }
プロパティとして宣言します。
interface ICar {
int Year { get; set; }
}
エリックリッペルトはそれを釘付けにしました、私は彼が言ったことを言うのに別の方法を使用します。インターフェースのメンバーはすべて仮想であり、インターフェースを継承するクラスによってすべてオーバーライドする必要があります。インターフェース宣言で仮想キーワードを明示的に記述したり、クラスでオーバーライドキーワードを使用したりしないでください。
virtualキーワードは、メソッドといわゆるvテーブル、メソッドポインターの配列を使用して.NETに実装されます。overrideキーワードは、v-tableスロットを別のメソッドポインターで埋め、基本クラスによって生成されたものを上書きします。プロパティ、イベント、インデクサーは、内部でメソッドとして実装されています。しかし、フィールドはそうではありません。したがって、インターフェースにフィールドを含めることはできません。
Year
完全に素晴らしいプロパティを持たないのはなぜですか?
フィールドはデータ表現の特定の実装を表すため、インターフェイスにはフィールドが含まれていません。フィールドを公開するとカプセル化が解除されます。したがって、フィールドを持つインターフェースを持つことは、インターフェースの代わりに実装に効果的にコーディングすることになります。これは、インターフェースが持つ奇妙なパラドックスです!
例えば、あなたの一部Year
仕様がために、それが無効であることを必要とするかもしれないICar
実装がに割り当てできるようにYear
、後に現在の年よりも+言うこと方法はありません1または1900年前に、あなたが露出していた場合Year
フィールド-はるかに優れて使用します代わりにここで作業を行うためのプロパティ。
短い答えは「はい」です。すべての実装タイプは、独自のバッキング変数を作成する必要があります。これは、インターフェイスがコントラクトに類似しているためです。それができることは、実装タイプが利用可能にしなければならない特定の公にアクセス可能なコードを指定することだけです。コード自体を含めることはできません。
あなたが提案することを使用してこのシナリオを考えてください:
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 { }
ここにいくつかの問題があります:
myBackingVariable
をMyClass
使用しますか?取られる最も一般的なアプローチは、インターフェースとそれを実装する最小限の抽象クラスを宣言することです。これにより、抽象クラスから継承して実装を無料で取得するか、インターフェイスを明示的に実装して別のクラスからの継承を許可するという柔軟性が得られます。次のように機能します。
public interface IMyInterface
{
int MyProperty { get; set; }
}
public abstract class MyInterfaceBase : IMyInterface
{
int myProperty;
public int MyProperty
{
get { return myProperty; }
set { myProperty = value; }
}
}
多くのことはすでに述べられていますが、簡単にするために、ここに私の見解を示します。インターフェイスは、コンシューマまたはクラスによって実装されるメソッドコントラクトを持ち、値を格納するフィールドを持たないことを目的としています。
では、なぜプロパティが許可されているのかと主張するかもしれません。したがって、簡単な答えは、プロパティは内部的にはメソッドとしてのみ定義されているということです。
このため、年フィールドを実装するCar基本クラスを作成でき、他のすべての実装はそれから継承できます。
インターフェイスは、パブリックインスタンスのプロパティとメソッドを定義します。フィールドは通常、プライベート、または多くても保護された内部、または保護された内部です(「フィールド」という用語は、通常、パブリックには使用されません)。
他の返信で述べたように、基本クラスを定義し、すべての継承者がアクセスできる保護されたプロパティを定義できます。
奇妙な点の1つは、実際にはインターフェイスを内部として定義できるが、インターフェイスの有用性を制限することであり、通常、他の外部コードでは使用されない内部機能を定義するために使用されます。