次の方法で共有


19 インターフェイス

19.1 全般

インターフェイスによりコントラクトが定義されます。 インターフェイスを実装するクラスまたは構造体は、その契約に従う必要があります。 インターフェイスは複数の基本インターフェイスから継承することができ、クラスまたは構造体は複数のインターフェイスを実装することができます。

インターフェイスには、 §19.4 で説明されているように、さまざまな種類のメンバーが含まれている場合があります。 インターフェイス自体は、宣言する関数メンバーの一部またはすべての実装を提供できます。 インターフェイスが実装を提供しないメンバーは抽象です。 これらの実装は、インターフェイスを実装するクラスまたは構造体、またはオーバーライドする定義を提供する派生インターフェイスによって提供される必要があります。

: これまで、インターフェイスに新しい関数メンバーを追加すると、そのインターフェイス型のすべての既存のコンシューマーに影響が及びます。それは破壊的変更でした。 インターフェイス関数メンバーの実装を追加することで、開発者はインターフェイスをアップグレードしながら、すべての実装者がその実装をオーバーライドできるようになります。 インターフェイスのユーザーは、非破壊的変更として実装を受け入れることができます。ただし、要件が異なる場合は、指定された実装をオーバーライドできます。 注釈

19.2 インターフェイス宣言

19.2.1 全般

interface_declaration は、新しいインターフェイス型を宣言する type_declaration (§14.7) です。

interface_declaration
    : attributes? interface_modifier* 'partial'? 'interface'
      identifier variant_type_parameter_list? interface_base?
      type_parameter_constraints_clause* interface_body ';'?
    ;

interface_declarationは、オプションの連の属性 (§23) と、オプションの連の interface_modifier (§19.2.2) の後に、オプションの部分修飾子 (§15.2.7) が続き、その後にキーワード interfaceとインターフェイスに名前を付けた識別子で構成されます。 その後に省略可能なvariant_type_parameter_list仕様 (§19.2.3) が続き、オプションのinterface_base仕様 (§19.2.4) が続きます)、省略可能なtype_parameter_constraints_clauseの仕様 (§15.2.5) の後に、interface_body (§19.3) が続き、必要に応じてセミコロンが続きます。

インターフェイス宣言は、 variant_type_parameter_listも提供していない限り、type_parameter_constraints_clauseを提供してはなりません。

variant_type_parameter_list を提供するインターフェイス宣言は、ジェネリック インターフェイス宣言です。 さらに、ジェネリック インターフェイス宣言またはジェネリック構造体宣言内に入れ子になったクラスは、それ自体がジェネリック インターフェイス宣言です。これは、包含型の型引数を指定して構築型 (§8.4) を作成する必要があるためです。

19.2.2 インターフェイス修飾子

interface_declaration には、必要に応じてインターフェイス修飾子のシーケンスを含めることができます。

interface_modifier
    : 'new'
    | 'public'
    | 'protected'
    | 'internal'
    | 'private'
    | unsafe_modifier   // unsafe code support
    ;

unsafe_modifier (§24.2) は安全でないコード (§24) でのみ使用できます。

インターフェイス宣言で同じ修飾子が複数回出現するのはコンパイル時のエラーです。

new 修飾子は、クラス内で定義されているインターフェイスでのみ許可されます。 §15.3.5 で説明されているように、インターフェイスが同じ名前で継承されたメンバーを非表示にすることを指定します。

publicprotectedinternal、および private 修飾子は、インターフェイスのアクセシビリティを制御します。 インターフェイス宣言が発生するコンテキストによっては、これらの修飾子の一部のみが許可される場合があります (§7.5.2)。 partial 型宣言 (§15.2.7) にアクセシビリティ仕様 (publicprotectedinternal、および private 修飾子を使用) が含まれている場合、§15.2.2 の規則が適用されます。

19.2.3 バリアント型パラメーター リスト

19.2.3.1 全般

バリアント型パラメーター リストは、インターフェイス型とデリゲート型でのみ使用できます。 通常の type_parameter_list との違いは、各型パラメーターの省略可能な variance_annotation です。

variant_type_parameter_list
    : '<' variant_type_parameter (',' variant_type_parameter)* '>'
    ;

variant_type_parameter
    : attributes? variance_annotation? type_parameter
    ;

variance_annotation
    : 'in'
    | 'out'
    ;

分散注釈が out の場合、型パラメーターは共変と見なされます。 分散注釈が in の場合、型パラメーターは反変と見なされます。 分散注釈がない場合、型パラメーターは不変と見なされます。

: 次の場合、

interface C<out X, in Y, Z>
{
    X M(Y y);
    Z P { get; set; }
}

X は共変であり、Y は反変であり、Z は不変です。

終了サンプル

ジェネリック インターフェイスが複数の部分で宣言されている場合 (§15.2.3)、各部分宣言では、各型パラメーターに同じ分散を指定する必要があります。

19.2.3.2 分散安全性

型の型パラメーター リストに分散注釈が出現すると、型宣言内で型が発生する可能性がある場所が制限されます。

次のいずれかである場合、型 T は output-unsafe です。

  • T は、反変の型パラメーターである。
  • T は、output-unsafe 要素型を持つ配列型である。
  • T は、ジェネリック型 Sᵢ,... Aₑ から構築されるインターフェイス型またはデリゲート型 S<Xᵢ, ... Xₑ> で、少なくとも 1 つの Aᵢ が次のいずれかを保持します。
    • Xᵢ は共変または不変であり、Aᵢ は output-unsafe である。
    • Xᵢ は反変または不変であり、Aᵢ は input-unsafe である。

次のいずれかである場合、型 T は input-unsafe です。

  • T は共変の型パラメーターである。
  • T は、input-unsafe 要素型を持つ配列型である。
  • T は、ジェネリック型 S<Aᵢ,... Aₑ> から構築されるインターフェイス型またはデリゲート型 S<Xᵢ, ... Xₑ> で、少なくとも 1 つの Aᵢ が次のいずれかを保持します。
    • Xᵢ は共変または不変であり、Aᵢ は input-unsafe である。
    • Xᵢ は反変または不変であり、Aᵢ は output-unsafe である。

直感的に、output-unsafe 型は出力位置では禁止され、input-unsafe 型は入力位置で禁止されます。

型は、output-unsafe でない場合は output-safe、input-unsafe でない場合は input-safe です。

19.2.3.3 分散変換

分散注釈の目的は、インターフェイス型とデリゲート型に対してより寛大な (ただし、安全な型) 変換を提供することです。 このために、暗黙的な (§10.2) と明示的な変換 (§10.3) の定義では、分散変換の概念が使用されます。これは次のように定義されています。

T<Aᵢ, ..., Aᵥ> がバリアント型パラメーター T<Bᵢ, ..., Bᵥ> で宣言されたインターフェイスまたはデリゲート型のいずれかであり、各バリアント型パラメーター T について次のいずれかである場合、型 T<Xᵢ, ..., Xᵥ> は、型 Xᵢ に対して分散変換可能です。

  • Xᵢ は共変であり、Aᵢ から Bᵢ への暗黙的な参照または同一性変換が存在する。
  • Xᵢ は反変であり、Bᵢ から Aᵢ への暗黙的な参照または同一性変換が存在する。
  • Xᵢ は不変であり、Aᵢ から Bᵢ への同一性変換が存在する。

19.2.4 基本インターフェイス

インターフェイスは、インターフェイスの 明示的な基本インターフェイスと呼ばれる、0 個以上のインターフェイス型から継承できます。 インターフェイスに 1 つ以上の明示的な基本インターフェイスがある場合、そのインターフェイスの宣言では、インターフェイス識別子の後に、基本インターフェイス型のコロンとコンマ区切りのリストが続きます。

派生インターフェイスでは、基本インターフェイスで宣言された継承されたメンバー (§7.7.2.3) を非表示にする新しいメンバーを宣言したり、基本インターフェイスで宣言された継承されたメンバー (§19.6.2) を明示的に実装したりできます。

interface_base
    : ':' interface_type_list
    ;

明示的な基本インターフェイスは、インターフェイス型 (§8.4§19.2) を構築できます。 基底インターフェイスは独自の型パラメーター型にすることはできませんが、スコープ内の型パラメーターを含めることができます。

構築されたインターフェイス型の場合、明示的な基本インターフェイスは、ジェネリック型宣言で明示的な基本インターフェイス宣言を受け取り、基本インターフェイス宣言の各 type_parameter に対して、構築された型の対応する type_argument を置き換えることによって形成されます。

インターフェイスの明示的な基本インターフェイスは、少なくともインターフェイス自体と同じかそれ以上のアクセスが可能である必要があります (§7.5.5)。

: たとえば、private インターフェイスの internal または public インターフェイスを指定するのはコンパイル時エラーです。 注釈

インターフェイスが直接的または間接的に自身を継承すると、コンパイル時にエラーが発生します。

インターフェイスの基本インターフェイスは、明示的な基本インターフェイスとその基本インターフェイスです。 つまり、基本インターフェイスのセットは、明示的な基本インターフェイス、その明示的な基本インターフェイスのように続く完全な推移的閉包です。 インターフェイスは、その基本インターフェイスのすべてのメンバーを継承します。

: 次のコード例の内容:

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

interface IListBox : IControl
{
    void SetItems(string[] items);
}

interface IComboBox: ITextBox, IListBox {}

IComboBox の基本インターフェイスは、IControlITextBox、および IListBox です。 言い換えると、上記の IComboBox インターフェイスは、メンバー SetTextSetItemsPaint を継承します。

終了サンプル

構築されたジェネリック型から継承されたメンバーは、型の置換後に継承されます。 つまり、メンバー内のすべての構成要素型には、class_base 仕様で使用される対応する型引数に置き換えらる、基底クラス宣言の型パラメーターがあります。

: 次のコード例の内容:

interface IBase<T>
{
    T[] Combine(T a, T b);
}

interface IDerived : IBase<string[,]>
{
    // Inherited: string[][,] Combine(string[,] a, string[,] b);
}

インターフェイス IDerived は、型パラメーター CombineT に置き換えられた後、string[,] メソッドを継承します。

終了サンプル

インターフェイスを実装するクラスまたは構造体は、インターフェイスのすべての基本インターフェイスも暗黙的に実装します。

部分インターフェイス宣言 (§15.2.7) の複数の部分でのインターフェイスの処理については、 §15.2.4.3 で詳しく説明しています。

インターフェイスのすべての基本インターフェイスは、出力セーフである必要があります (§19.2.3.2)。

19.3 インターフェイス本体

インターフェイスの interface_body は、インターフェイスのメンバーを定義します。

interface_body
    : '{' interface_member_declaration* '}'
    ;

19.4 インターフェイス メンバー

19.4.1 全般

インターフェイスのメンバーは、基本インターフェイスから継承されたメンバーと、インターフェイス自体によって宣言されたメンバーです。

interface_member_declaration
    : constant_declaration
    | field_declaration
    | method_declaration
    | property_declaration
    | event_declaration
    | indexer_declaration
    | static_constructor_declaration
    | operator_declaration
    | type_declaration
    ;

この句は、クラス (§15.3) のメンバーの説明をインターフェイスの制限で補強します。 Interface メンバーは、 member_declaration s と次の追加規則を使用して宣言されます。

  • finalizer_declarationは許可されていません。
  • インスタンス コンストラクター (constructor_declaration) は許可されません。
  • すべてのインターフェイス メンバーは、暗黙的にパブリック アクセス権を持ちます。ただし、静的コンストラクター (§15.12) を除き、明示的なアクセス修飾子 (§7.5.2) を使用できます。
  • abstract修飾子は、本体のないインターフェイス関数メンバーに対して暗黙的に指定されます。その修飾子は明示的に指定できます。
  • virtualまたはsealed修飾子を使用しない限り、本体を含む宣言が暗黙的にprivateメンバーであるインターフェイス インスタンス関数メンバー。 virtual修飾子は明示的に指定できます。
  • インターフェイスの private または sealed 関数メンバーは、本体を持つ必要があります。
  • private関数メンバーは、修飾子sealedを持つ必要はありません。
  • 派生インターフェイスは、基本インターフェイスで宣言された抽象メンバーまたは仮想メンバーをオーバーライドできます。
  • 明示的に実装された関数メンバーには、修飾子 sealedは含まれません。

constant_declaration (§15.4) などの一部の宣言では、インターフェイスに制限はありません。

インターフェイスの継承されたメンバーは、特にインターフェイスの宣言空間の一部ではありません。 したがって、インターフェイスは、継承されたメンバーと同じ名前またはシグネチャを持つメンバーを宣言できます。 この場合、派生インターフェイス メンバーは、基本インターフェイス メンバーを非表示するものとされています。 継承されたメンバーを非表示にしてもエラーとは見なされませんが、警告 (§7.7.2.3) になります。

継承されたメンバーを非表示にしない宣言に new 修飾子が含まれている場合は、その旨の警告が発行されます。

: クラス object のメンバーは、厳密に言えば、どのインターフェイスのメンバーでもあります (§19.4)。 ただし、クラス object のメンバーは、任意のインターフェイス型 (§12.5) のメンバー検索を介して使用できます。 注釈

複数の部分で宣言されたインターフェイスのメンバーのセット (§15.2.7) は、各部分で宣言されたメンバーの和集合です。 インターフェイス宣言のすべての部分の本体は同じ宣言空間 (§7.3) を共有し、各メンバーのスコープ (§7.7) はすべての部分の本体まで拡張します。

: メンバー IAとプロパティ Mの実装を含むインターフェイスPについて考えます。 実装型 C では、 M または Pの実装は提供されません。 コンパイル時の型が IA または IBに暗黙的に変換できるインターフェイスである参照を介してアクセスする必要があります。 これらのメンバーは、 C型の変数に対するメンバー参照では見つかりません。

interface IA
{
    public int P { get { return 10; } }
    public void M()
    {
        Console.WriteLine("IA.M");
    }
}

interface IB : IA
{
    public new int P { get { return 20; } }
    void IA.M()
    {
        Console.WriteLine("IB.M");
    }
}

class C : IB { }

class Test
{
    public static void Main()
    {
        C c = new C();
        ((IA)c).M();                               // cast needed
        Console.WriteLine($"IA.P = {((IA)c).P}");  // cast needed
        Console.WriteLine($"IB.P = {((IB)c).P}");  // cast needed
    }
}

インターフェイス IA および IB内では、メンバー M には名前で直接アクセスできます。 ただし、メソッド Main内では、 c.M()c.Pを記述することはできません。これらの名前は表示されないためです。 それらを見つけるには、適切なインターフェイス型へのキャストが必要です。 MでのIBの宣言では、明示的なインターフェイス実装構文が使用されます。 これは、そのメソッドが IA内のメソッドをオーバーライドするために必要です。修飾子 override は関数メンバーに適用されない可能性があります。 終了サンプル

19.4.2 インターフェイス フィールド

この句は、インターフェイスで宣言されたフィールドのクラス §15.5 のフィールドの説明を拡張します。

インターフェイス フィールドは、次の追加規則field_declaration (§15.5.1) を使用して宣言されます。

  • インスタンス フィールドを宣言 するfield_declaration のコンパイル時エラーです。

: 次のプログラムには、さまざまな種類の静的メンバーが含まれています。

public interface IX
{
    public const int Constant = 100;
    protected static int field;

    static IX()
    {
        Console.WriteLine("static members initialized");
        Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}");
        field = 50;
        Console.WriteLine("static constructor has run");
    }
}

public class Test: IX
{
    public static void Main()
    {
        Console.WriteLine($"constant = {IX.Constant}, field = {IX.field}");
    }
}

生成される出力は次のとおりです。

static members initialized
constant = 100, field = 0
static constructor has run
constant = 100, field = 50

終了サンプル

静的フィールドの割り当てと初期化については、 §19.4.8 を参照してください。

19.4.3 インターフェイスメソッド

この句は、インターフェイスで宣言されたメソッドのクラス §15.6 のメソッドの説明を拡張します。

インターフェイス メソッドは 、method_declarations (§15.6)) を使用して宣言されます。 インターフェイス メソッド宣言 の属性return_typeref_return_type識別子およびparameter_list は、クラス内のメソッド宣言の属性と同じ意味を持ちます。 インターフェイス メソッドには、次の追加規則があります。

  • method_modifierにはoverrideを含めてはならない。

  • 本文がセミコロン (;) であるメソッドが abstractabstract 修飾子は必要ありませんが、許可されます。

  • ブロック本体または式本体をmethod_bodyとして持つインターフェイス メソッド宣言はvirtualvirtual修飾子は必要ありませんが、許可されます。

  • method_declarationtype_parameter_listを持たない限り、type_parameter_constraints_clauseを持つものではありません。

  • クラス メソッドに指定された修飾子の有効な組み合わせの要件の一覧は、次のように拡張されます。

    • extern ではない静的宣言は、ブロック本体または式本体を method_bodyとして持つ必要があります。
    • extern ではない仮想宣言は、ブロック本体または式本体を method_bodyとして持つ必要があります。
    • extern ではないプライベート宣言は、ブロック本体または式本体を method_bodyとして持つ必要があります。
    • extern ではないシール宣言は、ブロック本体または式本体を method_bodyとして持つ必要があります。
    • 非同期宣言は、ブロック本体または式本体を method_bodyとして持つ必要があります。
  • インターフェイス メソッドのすべてのパラメーター型は、入力セーフ (§19.2.3.2) で、戻り値の型は void または出力セーフである必要があります。

  • 出力または参照パラメーターの型も、出力セーフである必要があります。

    : 出力パラメーターは、一般的な実装制限により、input-safe である必要があります。 注釈

  • メソッドの任意の型パラメーターに対する各クラス型制約、インターフェイス型制約、および型パラメーター制約は、入力セーフである必要があります。

これらの規則により、インターフェイスの共変または反変の使用が確実にタイプセーフで維持されます。

:

interface I<out T>
{
    void M<U>() where U : T;     // Error
}

これは、T の型パラメーター制約として U を使用することは input-safe.ではないため、不正な形式です。

この制限が適用されていない場合、次ように型の安全性が損なわれる可能性があります。

interface I<out T>
{
    void M<U>() where U : T;
}
class B {}
class D : B {}
class E : B {}
class C : I<D>
{
    public void M<D>() {...} 
}

...

I<B> b = new C();
b.M<E>();

これは実際には、C.M<E> への呼び出しです。 しかし、この呼び出しでは ED から派生している必要があるため、ここで型の安全性に違反します。

終了サンプル

: 実装を持つ静的メソッドを示すだけでなく、そのメソッドが呼び出され、正しい戻り値の型とシグネチャを持つため、エントリ ポイントでもある例については、Main を参照してください。 注釈

インターフェイスで宣言された実装を持つ仮想メソッドは、派生インターフェイスで抽象されるようにオーバーライドできます。 これは reabstraction と呼ばれます。

:

interface IA
{
    void M() { Console.WriteLine("IA.M"); }
}

interface IB: IA
{
    abstract void IA.M();    // reabstraction of M
}

これは、メソッドの実装が不適切であり、クラスを実装することによってより適切な実装を提供する必要がある派生インターフェイスで役立ちます。 終了サンプル

19.4.4 インターフェイスのプロパティ

この句は、インターフェイスで宣言されたプロパティのクラス §15.7 のプロパティの説明を拡張します。

インターフェイス プロパティは、次の追加規則を使用して property_declarations (§15.7.1) を使用して宣言されます。

  • property_modifierにはoverrideを含めてはならない。

  • 明示的なインターフェイス メンバーの実装には 、accessor_modifier (§15.7.3) を含めてはならない。

  • 派生インターフェイスは、基本インターフェイスで宣言された抽象インターフェイス プロパティを明示的に実装できます。

    : インターフェイスにはインスタンス フィールドを含めることができないので、暗黙的な非表示のインスタンス フィールドの宣言が必要になるので、インターフェイス プロパティをインスタンスの自動プロパティにすることはできません。 注釈

  • インターフェイス プロパティの型は、get アクセサーがある場合は output-safe であり、set アクセサーがある場合は input-safe である必要があります。

  • ブロック本体または式本体をmethod_bodyとして持つインターフェイス メソッド宣言はvirtualvirtual修飾子は必要ありませんが、許可されます。

  • 実装がないインスタンス property_declarationabstractabstract 修飾子は必要ありませんが、許可されます。 自動的に実装される プロパティと見 なされることはありません (§15.7.4)。

19.4.5 インターフェイス イベント

この句は、インターフェイスで宣言されたイベントのクラス §15.8 のイベントの説明を拡張します。

インターフェイス イベントは 、event_declarations (§15.8.1) を使用して宣言され、次の追加規則が使用されます。

  • event_modifier には overrideを含めてはならない。
  • 派生インターフェイスは、基本インターフェイス (§15.8.5) で宣言された抽象インターフェイス イベントを実装できます。
  • variable_initializerを含むインスタンスevent_declaration内のvariable_declaratorsのコンパイル時エラーです。
  • virtualまたはsealed修飾子を持つインスタンス イベントでは、アクセサーを宣言する必要があります。 自動的に実装されるフィールドに似たイベントとは見な されません (§15.8.2)。
  • abstract修飾子を持つインスタンス イベントでアクセサーを宣言することはできません。
  • インターフェイス イベントの型は、input-safe である必要があります。

19.4.6 インターフェイス インデクサー

この句は、インターフェイスで宣言されたインデクサーのクラス §15.9 のインデクサーの説明を拡張します。

インターフェイス インデクサーは indexer_declaration (§15.9) を使用して宣言され、次の規則が追加されます。

  • indexer_modifier には overrideを含めてはならない。

  • 式本体を持つindexer_declaration、またはブロック本体または式本体を持つアクセサーを含むindexer_declarationはvirtualvirtual修飾子は必要ありませんが、許可されます。

  • アクセサー本体がセミコロン (;) であるindexer_declarationがabstractabstract修飾子は必要ありませんが、許可されます。

  • インターフェイス インデクサーのすべてのパラメーター型は、入力セーフである必要があります (§19.2.3.2)。

  • 出力または参照パラメーターの型も、出力セーフである必要があります。

    : 出力パラメーターは、一般的な実装制限により、input-safe である必要があります。 注釈

  • インターフェイス インデクサーの型は、get アクセサーがある場合は output-safe であり、set アクセサーがある場合は input-safe である必要があります。

19.4.7 インターフェイス演算子

この句は、インターフェイスで宣言された 演算子のクラス§15.10 のoperator_declarationメンバーの説明を拡張します。

インターフェイスの operator_declaration は実装 (§19.1) です。

これは、変換、等値、または非等値演算子を宣言するインターフェイスのコンパイル時エラーです。

19.4.8 インターフェイスの静的コンストラクター

この句は、インターフェイスで宣言された静的コンストラクターのクラス §15.12 の静的コンストラクターの説明を拡張します。

閉じた (§8.4.3) インターフェイスの静的コンストラクターは、特定のアプリケーション ドメインで最大で 1 回実行されます。 静的コンストラクターの実行は、アプリケーション ドメイン内で発生する次のアクションの 1 つ目によってトリガーされます。

  • インターフェイスの静的メンバーのいずれかが参照されます。
  • 実行を開始するMain メソッド (Main) を含むインターフェイスに対して メソッドが呼び出される前。
  • そのインターフェイスはメンバーの実装を提供し、その実装は、そのメンバーの最も具体的な実装 (§19.4.10) としてアクセスされます。

: 上記のどのアクションも実行されない場合、インターフェイスを実装する型のインスタンスが作成されて使用されるプログラムに対して、インターフェイスの静的コンストラクターが実行されない場合があります。 注釈

新しい閉じたインターフェイス型を初期化するには、まず、その特定の閉じた型の静的フィールドの新しいセットが作成されます。 各静的フィールドは既定値に初期化されます。 次に、これらの静的フィールドに対して静的フィールド初期化子が実行されます。 最後に、静的コンストラクターが実行されます。

: インターフェイス内で宣言されたさまざまな種類の静的メンバー (Main メソッドを含む) の使用例については、 §19.4.2 を参照してください。 注釈

19.4.9 インターフェイスの入れ子になった型

この句は、インターフェイスで宣言された入れ子になった型のクラス §15.3.9 の入れ子になった型の説明を拡張します。

variance_annotation (§19.2.3.1) で宣言された型パラメーターのスコープ内でクラス型、構造体型、または列挙型を宣言するとエラーになります。

: 次の C の宣言はエラーです。

interface IOuter<out T>
{
    class C { } // error: class declaration within scope of variant type parameter 'T'
}

終了サンプル

19.4.10 最も具体的な実装

すべてのクラスと構造体には、型またはその直接および間接インターフェイスに出現する実装のうち、その型によって実装されるすべてのインターフェイスで宣言されているすべての仮想メンバーに対して、最も具体的な実装が必要です。 最も具体的な実装は、他のすべての実装よりも具体的な一意の実装です。

: 最も具体的な実装規則により、ダイヤモンド インターフェイスの継承に起因するあいまいさが、競合が発生した時点でプログラマによって明示的に解決されます。 注釈

TI2の両方が、メンバー I3を宣言するインターフェイス I2I3から直接または間接的に派生する構造体またはIインターフェイスを実装するクラスである型Mの場合、Mの最も具体的な実装は次のとおりです。

  • TI.Mの実装を宣言する場合、その実装は最も具体的な実装です。
  • それ以外の場合、 T がクラスであり、直接基底クラスまたは間接基底クラスが I.Mの実装を宣言する場合、 T の最も派生した基底クラスが最も具体的な実装になります。
  • それ以外の場合、 I2I3T によって実装され、 I3 が直接または間接的に I2 から派生している場合、 I3.MI2.Mよりも具体的な実装です。
  • それ以外の場合、 I2.MI3.M もより具体的なものではなく、エラーが発生します。

:

interface IA
{
    void M() { Console.WriteLine("IA.M"); }
}

interface IB : IA
{
    void IA.M() { Console.WriteLine("IB.M"); }
}

interface IC: IA
{
    void IA.M() { Console.WriteLine("IC.M"); }
}

abstract class C: IB, IC { } // error: no most specific implementation for 'IA.M'

abstract class D: IA, IB, IC // OK
{
    public abstract void M();
}

最も具体的な実装規則により、競合 (つまり、ダイヤモンドの継承から生じるあいまいさ) が、競合が発生した時点でプログラマによって明示的に解決されます。 終了サンプル

19.4.11 インターフェイス メンバー アクセス

インターフェイス メンバーは、メンバー アクセス (§12.8.7) とインデクサー アクセス (§12.8.12.4) のフォーム I.MI[A]の式を使用してアクセスされます。ここで、 I はインターフェイス型、 M はそのインターフェイス型の定数、フィールド、メソッド、プロパティ、またはイベントであり、 A はインデクサー引数リストです。

直接または間接的な基底クラスDを使用するクラス Bでは、Bはインターフェイス Iを直接または間接的に実装し、IはメソッドM()を定義します。式base.M()は、クラス型のbase.M()の実装静的にバインドされている場合にのみ有効です。M()

厳密に単一継承であるインターフェイスの場合 (継承チェーン内の各インターフェイスには、メンバー参照 (§12.5)、メソッド呼び出し (§12.8.10.2)、およびインデクサー アクセス (§12.8.12.4) ルールの効果は、クラスと構造体の場合とまったく同じです。より多くの派生メンバーは、同じ名前またはシグネチャを持つ派生メンバーを非表示にします。 ただし、複数継承インターフェイスの場合、関連のない 2 つ以上の基本インターフェイスが同じ名前またはシグネチャを持つメンバーを宣言すると、あいまいさが発生する可能性があります。 この小項では、いくつかの例を示しています。そのうちの一部は曖昧さを引き起こすもので、他の例は引き起こさないものです。 いずれの場合も、明示的なキャストを使用してあいまいさを解決できます。

: 次のコード例の内容:

interface IList
{
    int Count { get; set; }
}

interface ICounter
{
    int Count { get; set; }
}

interface IListCounter : IList, ICounter {}

class C
{
    void Test(IListCounter x)
    {
        x.Count = 1;             // Error
        ((IList)x).Count = 1;    // Ok, invokes IList.Count.set
        ((ICounter)x).Count = 1; // Ok, invokes ICounter.Count
    }
}

最初のステートメントでは、Countのメンバー参照 (IListCounter) があいまいであるため、コンパイル時エラーが発生します。 例で示すように、あいまいさは、適切な基本インターフェイス型に x をキャストすることによって解決されます。 このようなキャストには実行時コストはかかりません。コンパイル時にインスタンスを派生の少ない型として表示するだけです。

終了サンプル

: 次のコード例の内容:

interface IInteger
{
    void Add(int i);
}

interface IDouble
{
    void Add(double d);
}

interface INumber : IInteger, IDouble {}

class C
{
    void Test(INumber n)
    {
        n.Add(1);             // Invokes IInteger.Add
        n.Add(1.0);           // Only IDouble.Add is applicable
        ((IInteger)n).Add(1); // Only IInteger.Add is a candidate
        ((IDouble)n).Add(1);  // Only IDouble.Add is a candidate
    }
}

呼び出し n.Add(1) は、IInteger.Add のオーバーロード解決規則を適用して を選択します。 同様に、呼び出し n.Add(1.0)IDouble.Add を選択します。 明示的なキャストが挿入されると、候補メソッドは 1 つしか存在しないため、あいまいさは生じません。

終了サンプル

: 次のコード例の内容:

interface IBase
{
    void F(int i);
}

interface ILeft : IBase
{
    new void F(int i);
}

interface IRight : IBase
{
    void G();
}

interface IDerived : ILeft, IRight {}

class A
{
    void Test(IDerived d)
    {
        d.F(1);           // Invokes ILeft.F
        ((IBase)d).F(1);  // Invokes IBase.F
        ((ILeft)d).F(1);  // Invokes ILeft.F
        ((IRight)d).F(1); // Invokes IBase.F
    }
}

IBase.F メンバーは、ILeft.F メンバーによって非表示にされます。 したがって、d.F(1) は、ILeft.F を経由するアクセス パスで非表示にされないように見えますが、呼び出し IBase.FIRight を選択します。

複数継承インターフェイスで非表示にするための直感的なルールは、単純に次のとおりです。メンバーが任意のアクセス パスで非表示の場合、すべてのアクセス パスで非表示になります。 IDerived から ILeft 経由で IBase へのアクセス パスは、IBase.Fを非表示にするため、IDerived から IRight 経由での IBase へのアクセス パスでもそのメンバーは非表示になります。

終了サンプル

19.5 修飾インターフェイス メンバー名

インターフェイス メンバーは、修飾インターフェイス メンバー名によって参照されることがあります。 インターフェイス メンバーの修飾名は、メンバーが宣言されているインターフェイスの名前の後にドット、その後にメンバーの名前が続く形で構成されます。 メンバーの修飾名は、メンバーが宣言されているインターフェイスを参照します。

: 次の宣言を指定した場合

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

Paint の修飾名は IControl.Paint であり、SetText の修飾名は ITextBox.SetText です。 上記の例では、Paint としてを ITextBox.Paint 参照することはできません。

終了サンプル

インターフェイスが名前空間の一部である場合、修飾インターフェイス メンバー名に名前空間名を含めることができます。

:

namespace System
{
    public interface ICloneable
    {
        object Clone();
    }
}

System名前空間内では、ICloneable.CloneSystem.ICloneable.Clone の両方が、Clone メソッドの修飾インターフェイス メンバー名です。

終了サンプル

19.6 インターフェイスの実装

19.6.1 全般

インターフェイスは、クラスと構造体によって実装できます。 クラスまたは構造体がインターフェイスを直接実装することを示すには、そのインターフェースをクラスまたは構造体の基底クラス リストに含めます。

インターフェイスC実装するクラスまたは構造体Iは、IがアクセスできるCで宣言されたすべてのメンバーの実装を提供または継承する必要があります。 Iのパブリック メンバーは、Cのパブリック メンバーで定義できます。 IでアクセスできるCで宣言された非パブリック メンバーは、明示的なインターフェイス実装 (C) を使用してで定義できます。

インターフェイス マッピング (§19.6.5) を満たすが、一致する基本インターフェイス メンバーを実装していない派生型のメンバーは、新しいメンバーを導入します。 これは、インターフェイス メンバーを定義するために明示的なインターフェイス実装が必要な場合に発生します。

:

interface ICloneable
{
    object Clone();
}

interface IComparable
{
    int CompareTo(object other);
}

class ListEntry : ICloneable, IComparable
{
    public object Clone() {...}    
    public int CompareTo(object other) {...}
}

終了サンプル

インターフェイスを直接実装するクラスまたは構造体は、インターフェイスのすべての基本インターフェイスも暗黙的に実装します。 これは、クラスまたは構造体が基底クラス リスト内のすべての基本インターフェイスを明示的に一覧表示しない場合でも同様です。

:

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

class TextBox : ITextBox
{
    public void Paint() {...}
    public void SetText(string text) {...}
}

ここでは、クラス TextBoxIControlITextBox の両方を実装します。

終了サンプル

クラス C がインターフェイスを直接実装すると、C から派生したすべてのクラスも暗黙的に実装されます。

クラス宣言で指定された基本インターフェイスは、インターフェイス型 (§8.4§19.2) を構築できます。

: 次のコードは、クラスが構築されたインターフェイス型を実装する方法を示しています。

class C<U, V> {}
interface I1<V> {}
class D : C<string, int>, I1<string> {}
class E<T> : C<int, T>, I1<T> {}

終了サンプル

ジェネリック クラス宣言の基本インターフェイスは、 §19.6.3 で説明されている一意性規則を満たす必要があります。

19.6.2 明示的なインターフェイス メンバーの実装

インターフェイスを実装するために、クラス、構造体、またはインターフェイスは 、明示的なインターフェイス メンバー実装を宣言できます。 明示的なインターフェイス メンバーの実装は、修飾インターフェイス メンバー名を参照するメソッド、プロパティ、イベント、またはインデクサーの宣言です。 基本インターフェイスで非パブリック メンバーを実装するクラスまたは構造体は、明示的なインターフェイス メンバーの実装を宣言する必要があります。 基本インターフェイスでメンバーを実装するインターフェイスでは、明示的なインターフェイス メンバーの実装を宣言する必要があります。

インターフェイス マッピングを満たす派生インターフェイス メンバー (§19.6.5) は、基本インターフェイス メンバー (§7.7.2) を非表示にします。 new修飾子が存在しない限り、コンパイラは警告を発行します。

:

interface IList<T>
{
    T[] GetElements();
}

interface IDictionary<K, V>
{
    V this[K key] { get; }
    void Add(K key, V value);
}

class List<T> : IList<T>, IDictionary<int, T>
{
    public T[] GetElements() {...}
    T IDictionary<int, T>.this[int index] {...}
    void IDictionary<int, T>.Add(int index, T value) {...}
}

ここでは、IDictionary<int,T>.thisIDictionary<int,T>.Add が明示的なインターフェイス メンバーの実装です。

終了サンプル

: 場合によっては、インターフェイス メンバーの名前が実装クラスに適していない場合があります。その場合、インターフェイス メンバーは明示的なインターフェイス メンバーの実装を使用して実装できます。 たとえば、ファイル抽象化を実装するクラスは、ファイル リソースを解放する効果がある Close メンバー関数を実装し、明示的なインターフェイス メンバー実装を使用して Dispose インターフェイスの IDisposable メソッドを実装する可能性があります。

interface IDisposable
{
    void Dispose();
}

class MyFile : IDisposable
{
    void IDisposable.Dispose() => Close();

    public void Close()
    {
        // Do what's necessary to close the file
        System.GC.SuppressFinalize(this);
    }
}

終了サンプル

メソッド呼び出し、プロパティ アクセス、イベント アクセス、またはインデクサー アクセスでは、修飾インターフェイス メンバー名を使用して明示的なインターフェイス メンバーの実装にアクセスすることはできません。 明示的なインターフェイス インスタンス メンバーの実装は、インターフェイス インスタンスを介してのみアクセスでき、その場合はメンバー名だけで参照されます。 明示的なインターフェイスの静的メンバーの実装には、インターフェイス名を介してのみアクセスできます。

明示的なインターフェイス メンバーの実装で、 または extern 以外の修飾子 (async) を含めるのは、コンパイル時エラーです。

明示的なインターフェイス メソッドの実装は、インターフェイスから任意の型パラメーター制約を継承します。

明示的なインターフェイス メソッド実装におけるtype_parameter_constraints_clauseは、classまたはstructを含み、それが継承された制約によって参照型または値型のいずれかであることが判明しているtype_parameterprimary_constraintを適用する場合に限られます。 明示的なインターフェイス メソッド実装のシグネチャに T? 形式の型 ( T は型パラメーター) は、次のように解釈されます。

  • 型パラメーターclassT制約が追加された場合、T?は null 許容参照型になります。それ以外の場合
  • 制約が追加されていない場合、またはstruct制約が追加された場合、型パラメーターTT?は null 許容値型になります。

: 型パラメーターが関係する場合のルールのしくみを次に示します。

#nullable enable
interface I
{
    void Foo<T>(T? value) where T : class;
    void Foo<T>(T? value) where T : struct;
}

class C : I
{
    void I.Foo<T>(T? value) where T : class { }
    void I.Foo<T>(T? value) where T : struct { }
}

型パラメーター制約 where T : classしないと、参照型型パラメーターを持つ基本メソッドをオーバーライドできません。 終了サンプル

: 明示的なインターフェイス メンバーの実装には、他のメンバーとは異なるアクセシビリティ特性があります。 明示的なインターフェイス メンバーの実装は、メソッド呼び出しまたはプロパティ アクセスで修飾インターフェイス メンバー名を介してアクセスすることはないため、ある意味プライベートです。 ただし、インターフェイスを介してアクセスできるため、宣言されているインターフェイスと同様にパブリックでもあります。 明示的なインターフェイス メンバーの実装は、次の 2 つの主な目的を果たします。

  • 明示的なインターフェイス メンバーの実装はクラスまたは構造体インスタンスを介してアクセスできないため、インターフェイス実装をクラスまたは構造体のパブリック インターフェイスから除外できます。 これは、クラスまたは構造体が、そのクラスまたは構造体のコンシューマーにとって関心のない内部インターフェイスを実装する場合に特に便利です。
  • 明示的なインターフェイス メンバー実装では、同じシグネチャを持つインターフェイス メンバーのあいまいさを解消できます。 明示的なインターフェイス メンバーの実装がないと、クラス、構造体、またはインターフェイスが同じシグネチャと戻り値の型を持つインターフェイス メンバーの異なる実装を持つことは不可能です。これは、クラス、構造体、またはインターフェイスが、同じシグネチャを持ち、異なる戻り値の型を持つすべてのインターフェイス メンバーに実装を持つことは不可能です。

注釈

明示的なインターフェイス メンバーの実装を有効にするには、クラス、構造体、またはインターフェイスは、修飾インターフェイス メンバー名、型、型パラメーターの数、およびパラメーター型が明示的なインターフェイス メンバー実装のものと正確に一致するメンバーを含む基底クラスまたは基本インターフェイス リストのインターフェイスに名前を付ける必要があります。 インターフェイス関数メンバーにパラメーター配列がある場合、関連付けられている明示的インターフェイス メンバー実装の対応するパラメーターは、params 修飾子を持つことが許可されますが、必須ではありません。 インターフェイス関数メンバーにパラメーター配列がない場合、関連付けられている明示的インターフェイス メンバーの実装には、パラメーター配列は含まれません。

: したがって、次のクラスでは

class Shape : ICloneable
{
    object ICloneable.Clone() {...}
    int IComparable.CompareTo(object other) {...} // invalid
}

IComparable.CompareTo の宣言は、IComparableShape の基底クラスの一覧にリストされておらず、ICloneable の基本インターフェイスではないため、コンパイル時エラーが発生します。 同様に、次の宣言では

class Shape : ICloneable
{
    object ICloneable.Clone() {...}
}

class Ellipse : Shape
{
    object ICloneable.Clone() {...} // invalid
}

ICloneable.Clone の基底クラス リストに Ellipse が明示的にリストされていないため、ICloneableEllipse の宣言によってコンパイル時エラーが発生します。

終了サンプル

明示的インターフェイス メンバー実装の修飾インターフェイス メンバー名は、メンバーが宣言されたインターフェイスを参照する必要があります。

: したがって、次の宣言では

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

class TextBox : ITextBox
{
    void IControl.Paint() {...}
    void ITextBox.SetText(string text) {...}
}

Paint の明示的インターフェイス メンバーの実装は、IControl.Paint ではなく、ITextBox.Paint と して記述する必要があります。

終了サンプル

19.6.3 実装インターフェイスの一意性

ジェネリック型宣言によって実装されるインターフェイスは、構築可能なすべての型に対して一意である必要があります。 この規則がないと、構築された特定の型を呼び出す正しいメソッドを特定することができません。

: ジェネリック クラス宣言を次のように記述することが許可されているとします。

interface I<T>
{
    void F();
}

class X<U ,V> : I<U>, I<V> // Error: I<U> and I<V> conflict
{
    void I<U>.F() {...}
    void I<V>.F() {...}
}

これが許可されている場合、次の場合に実行すべきコードを特定することができません。

I<int> x = new X<int, int>();
x.F();

終了サンプル

ジェネリック型宣言のインターフェイス リストが有効かどうかを判断するには、次の手順を実行します。

  • ジェネリック クラス、構造体、またはインターフェイス宣言 L で直接指定されたインターフェイスの一覧を C とします。
  • L に既に存在するインターフェイスの基本インターフェイスを L に追加します。
  • L から重複を削除します。
  • C から作成された可能な構築型が、型引数を L に置き換えた後、L の 2 つのインターフェイスが同一になる場合、C の宣言は無効です。 制約宣言は、構築可能なすべての型を判断する際に考慮されません。

: 上記 X クラス宣言では、インターフェイス リスト Ll<U>I<V> で構成されます。 U を持ち、V が同じ型である構築された型では、これら 2 つのインターフェイスが同一の型になるため、この宣言は無効です。 注釈

異なる継承レベルで指定されたインターフェイスが統合される場合があります。

interface I<T>
{
    void F();
}

class Base<U> : I<U>
{
    void I<U>.F() {...}
}

class Derived<U, V> : Base<U>, I<V> // Ok
{
    void I<V>.F() {...}
}

このコードは、Derived<U,V>I<U>I<V> の両方を実装している場合でも有効です。 コード

I<int> x = new Derived<int, int>();
x.F();

DerivedDerived<int,int>'を効果的に再実装するため、I<int>でメソッドを呼び出します (§19.6.7)。

19.6.4 ジェネリック メソッドの実装

ジェネリック メソッドが暗黙的にインターフェースのメソッドを実装する場合、各メソッドの型パラメータに与えられる制約は、インターフェースの型パラメータが適切な型引数に置き換えられた後の両方の宣言において等価でなければなりません。メソッドの型パラメータは、左から右の順序で識別されます。

: 次のコード例では

interface I<X, Y, Z>
{
    void F<T>(T t) where T : X;
    void G<T>(T t) where T : Y;
    void H<T>(T t) where T : Z;
}

class C : I<object, C, string>
{
    public void F<T>(T t) {...}                  // Ok
    public void G<T>(T t) where T : C {...}      // Ok
    public void H<T>(T t) where T : string {...} // Error
}

メソッド C.F<T> は、I<object,C,string>.F<T> を暗黙的に実装しています。 この場合、C.F<T> はすべての型パラメーターに対する暗黙的な制約であるため、T: object は、制約 object を指定する必要はありません (また許可もされていません)。 このメソッド C.G<T> は、インターフェイスの型パラメーターが対応する型引数に置き換えられた後、制約がインターフェイス内のものと一致するため、I<object,C,string>.G<T> を暗黙的に実装します。 シール型 (この場合は C.H<T>) を制約として使用できないため、メソッド string の制約はエラーです。 暗黙的インターフェイス メソッドの実装の制約は一致する必要があるため、制約を省略してもエラーになります。 したがって、I<object,C,string>.H<T> を暗黙的に実装することはできません。 このインターフェイス メソッドは、明示的インターフェイス メンバーの実装を使用してのみ実装できます。

class C : I<object, C, string>
{
    ...
    public void H<U>(U u) where U : class {...}

    void I<object, C, string>.H<T>(T t)
    {
        string s = t; // Ok
        H<T>(t);
    }
}

この場合、明示的インターフェイス メンバーの実装は、より緩い制約を持つパブリック メソッドを呼び出します。 t から s への代入は有効です。これは、この制約がソース コードでは表現できない場合でも、TT: string の制約を継承するためです。 終了サンプル

: ジェネリック メソッドがインターフェイス メソッドを明示的に実装する場合、実装メソッド (§15.7.1、§19.6.2) で制約は許可されません。 注釈

19.6.5 インターフェイスマッピング

クラスまたは構造体は、クラスまたは構造体の基底クラスリストにリストされているインターフェイスのすべての抽象メンバーの実装を提供する必要があります。 実装するクラスまたは構造体でインターフェイス メンバーの実装を検索するプロセスは、インターフェイス マッピングと呼ばれます。

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

  • S および I に一致する明示的なインターフェイス メンバー実装の宣言が M に含まれている場合、このメンバーは I.M の実装です。
  • それ以外の場合、S に、M と一致する非静的パブリック メンバーの宣言が含まれている場合、このメンバーは I.M の実装です。 複数のメンバーが一致する場合、どのメンバーが I.M の実装であるかは未指定です。 この状況は、S が、ジェネリック型で宣言された 2 つのメンバーが異なるシグネチャを持つが、型引数によってシグネチャが同一になる構築された型である場合にのみ、発生します。

C の基底クラス リストで指定されたすべてのインターフェイスのすべてのメンバーに対して実装を配置できない場合、コンパイル時エラーが発生します。 インターフェイスのメンバーには、基本インターフェイスから継承されたメンバーが含まれます。

構築されたインターフェイス型のメンバーは、§15.3.3 で指定されている対応する型引数に置き換えられた型パラメーターがあると見なされます。

: たとえば、次のジェネリック インターフェイス宣言の場合

interface I<T>
{
    T F(int x, T[,] y);
    T this[int y] { get; }
}

構築されたインターフェイス I<string[]> には、次のメンバーがあります。

string[] F(int x, string[,][] y);
string[] this[int y] { get; }

終了サンプル

インターフェイス マッピングの目的で、クラス、インターフェイス、または構造体メンバー A は、次の場合に B インターフェイス メンバーと一致します。

  • AB がメソッドであり、AB の名前、型、およびパラメーター リストが同一である。
  • AB がプロパティであり、AB の名前および型が同一で、AB のアクセサーが同一である場合 (A がインターフェイス メンバーの明示的な実装でない場合は、追加のアクセサーが許可されます)。
  • AB がイベントであり、AB の名前および型が同一である場合。
  • AB がインデクサーであり、AB の型およびパラメーター リストが同一で、AB のアクセサーが同一である (A がインターフェイス メンバーの明示的な実装でない場合は、追加のアクセサーが許可されます)。

インターフェイス マッピング アルゴリズムの主な影響を以下に挙げています。

  • インターフェイス メンバーを実装するクラスまたは構造体メンバーを決定する場合、明示的なインターフェイス メンバーの実装は、同じクラスまたは構造体内の他のメンバーよりも優先されます。
  • 非パブリック メンバーも静的メンバーも、インターフェイス マッピングには参加しません。

: 次のコード例の内容:

interface ICloneable
{
    object Clone();
}

class C : ICloneable
{
    object ICloneable.Clone() {...}
    public object Clone() {...}
}

明示的なインターフェイス メンバーの実装が他のメンバーよりも優先されるため、ICloneable.CloneC メンバーが CloneICloneable の実装になります。

終了サンプル

クラスまたは構造体が、同じ名前、型、およびパラメーター型のメンバーを含む 2 つ以上のインターフェイスを実装する場合、これらの各インターフェイス メンバーを 1 つのクラスまたは構造体メンバーにマップできます。

:

interface IControl
{
    void Paint();
}

interface IForm
{
    void Paint();
}

class Page : IControl, IForm
{
    public void Paint() {...}
}

ここでは、PaintIControl の両方の IForm メソッドが、PaintPage メソッドにマップされます。 もちろん、2 つのメソッドに対して個別の明示的なインターフェイス メンバー実装を持つことも可能です。

終了サンプル

クラスまたは構造体が非表示のメンバーを含むインターフェイスを実装する場合、一部のメンバーは明示的インターフェイス メンバーの実装を通じて実装する必要があります。

:

interface IBase
{
    int P { get; }
}

interface IDerived : IBase
{
    new int P();
}

このインターフェイスの実装では、少なくとも 1 つの明示的インターフェイス メンバーの実装が必要で、次のいずれかの形式になります。

class C1 : IDerived
{
    int IBase.P { get; }
    int IDerived.P() {...}
}
class C2 : IDerived
{
    public int P { get; }
    int IDerived.P() {...}
}
class C3 : IDerived
{
    int IBase.P { get; }
    public int P() {...}
}

終了サンプル

クラスが同じ基本インターフェイスを持つ複数のインターフェイスを実装する場合、基本インターフェイスの実装は 1 つだけです。

: 次のコード例の内容:

interface IControl
{
    void Paint();
}

interface ITextBox : IControl
{
    void SetText(string text);
}

interface IListBox : IControl
{
    void SetItems(string[] items);
}

class ComboBox : IControl, ITextBox, IListBox
{
    void IControl.Paint() {...}
    void ITextBox.SetText(string text) {...}
    void IListBox.SetItems(string[] items) {...}
}

基底クラス リストで名前が指定された IControlIControl が継承する ITextBox、および IControl が継承する IListBox を個別に実装することはできません。 実際、これらのインターフェイスには別個の ID という概念はありません。 その代わり、ITextBoxIListBox の実装は IControl の同じ実装を共有し、ComboBox は単に、IControlITextBox、および IListBox の 3 つのインターフェイスを実装すると見なされます。

終了サンプル

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

: 次のコード例の内容:

interface Interface1
{
    void F();
}

class Class1
{
    public void F() {}
    public void G() {}
}

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

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

終了サンプル

19.6.6 インターフェイス実装の継承

クラスは、基底クラスによって提供されるすべてのインターフェイス実装を継承します。

インターフェイスを明示的に再実装しないと、派生クラスは基底クラスから継承するインターフェイス マッピングを変更できません。

: 次の宣言では

interface IControl
{
    void Paint();
}

class Control : IControl
{
    public void Paint() {...}
}

class TextBox : Control
{
    public new void Paint() {...}
}

PaintTextBox メソッドは PaintControl メソッドを非表示にしますが、Control.Paint への IControl.Paint のマッピングを変更せず、クラス インスタンスとインターフェイス インスタンスを介した Paint の呼び出しは、次の効果をもたらします

Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();  // invokes Control.Paint();
t.Paint();  // invokes TextBox.Paint();
ic.Paint(); // invokes Control.Paint();
it.Paint(); // invokes Control.Paint();

終了サンプル

ただし、インターフェイス メソッドがクラス内の仮想メソッドにマップされている場合、派生クラスで仮想メソッドをオーバーライドし、インターフェイスの実装を変更することが可能です。

: 上記の宣言を次のように書き換えると

interface IControl
{
    void Paint();
}

class Control : IControl
{
    public virtual void Paint() {...}
}

class TextBox : Control
{
    public override void Paint() {...}
}

次のように作用します

Control c = new Control();
TextBox t = new TextBox();
IControl ic = c;
IControl it = t;
c.Paint();  // invokes Control.Paint();
t.Paint();  // invokes TextBox.Paint();
ic.Paint(); // invokes Control.Paint();
it.Paint(); // invokes TextBox.Paint();

終了サンプル

明示的なインターフェイス メンバーの実装は仮想として宣言できないため、明示的なインターフェイス メンバーの実装をオーバーライドすることはできません。 ただし、明示的なインターフェイス メンバーの実装で別のメソッドを呼び出すことは有効で、その別のメソッドを仮想として宣言して、派生クラスによるオーバーライドを許可することができます。

:

interface IControl
{
    void Paint();
}

class Control : IControl
{
    void IControl.Paint() { PaintControl(); }
    protected virtual void PaintControl() {...}
}

class TextBox : Control
{
    protected override void PaintControl() {...}
}

ここでは、Control から派生したクラスは、IControl.Paint メソッドをオーバーライドすることによって PaintControl の実装を特殊化できます。

終了サンプル

19.6.7 インターフェイスの再実装

インターフェイス実装を継承するクラスは、基底クラスの一覧に含めることでインターフェイスを再実装できます。

インターフェイスの再実装は、インターフェイスの初期実装とまったく同じインターフェイス マッピング規則に従います。 したがって、継承インターフェイス マッピングは、インターフェイスの再実装用に確立されたインターフェイス マッピングには何の影響も与えません。

: 次の宣言では

interface IControl
{
    void Paint();
}

class Control : IControl
{
    void IControl.Paint() {...}
}

class MyControl : Control, IControl
{
    public void Paint() {}
}

ControlIControl.PaintControl.IControl.Paint にマップしても、MyControlIControl.Paint にマップする MyControl.Paint での再実装には影響しません。

終了サンプル

継承されたパブリック メンバー宣言と継承された明示的なインターフェイス メンバー宣言は、再実装されたインターフェイスのインターフェイス マッピング プロセスに参加します。

:

interface IMethods
{
    void F();
    void G();
    void H();
    void I();
}

class Base : IMethods
{
    void IMethods.F() {}
    void IMethods.G() {}
    public void H() {}
    public void I() {}
}

class Derived : Base, IMethods
{
    public void F() {}
    void IMethods.H() {}
}

この場合、IMethods での Derived の実装により、インターフェイス メソッドが Derived.FBase.IMethods.GDerived.IMethods.H、および Base.I にマップされます。

終了サンプル

クラスがインターフェイスを実装すると、そのインターフェイスのすべての基本インターフェイスも暗黙的に実装されます。 同様に、インターフェイスの再実装も暗黙的に、インターフェイスのすべての基本インターフェイスの再実装となります。

:

interface IBase
{
    void F();
}

interface IDerived : IBase
{
    void G();
}

class C : IDerived
{
    void IBase.F() {...}
    void IDerived.G() {...}
}

class D : C, IDerived
{
    public void F() {...}
    public void G() {...}
}

この場合、IDerived の再実装は、IBase も再実装し、IBase.FD.F にマッピングします。

終了サンプル

19.6.8 抽象クラスとインターフェイス

抽象クラス以外のクラスと同様に、抽象クラスは、クラスの基底クラスリストにリストされているインターフェイスのすべての抽象メンバーの実装を提供する必要があります。 ただし、抽象クラスでは、インターフェイス メソッドを抽象メソッドにマップすることが許可されています。

:

interface IMethods
{
    void F();
    void G();
}

abstract class C : IMethods
{
    public abstract void F();
    public abstract void G();
    }

ここでは、IMethods の実装は、FG を抽象メソッドにマップします。これは、C から派生する非抽象クラスでオーバーライドされる必要があります。

終了サンプル

明示的なインターフェイス メンバーの実装を抽象にすることはできませんが、明示的なインターフェイス メンバーの実装は抽象メソッドを呼び出すことが当然ながら許可されています。

:

interface IMethods
{
    void F();
    void G();
}

abstract class C: IMethods
{
    void IMethods.F() { FF(); }
    void IMethods.G() { GG(); }
    protected abstract void FF();
    protected abstract void GG();
}

この場合は、C から派生する非抽象クラスは、FFGG をオーバーライドする必要があり、これにより IMethods の実際の実装が提供されます。

終了サンプル