次の方法で共有


インターフェイス - 複数の型の動作を定義する

インターフェイスには、非抽象 class または struct で実装する必要がある、関連する機能のグループに対する定義が含まれます。 インターフェイスでは、 static メソッドを定義できます。 インターフェイスは、メンバーの既定の実装を定義できます。 インターフェイスでは、フィールド、自動的に実装されるプロパティ、プロパティのようなイベントなどのインスタンス データを宣言することはできません。

インターフェイスを使用すると、たとえば、クラス内の複数のソースからの動作を含めることができます。 C# ではクラスの複数の継承がサポートされないため、この機能は重要です。 また、構造体の継承をシミュレートする場合はインターフェイスを使用する必要があります。これは、実際に別の構造体またはクラスから継承することができないためです。

インターフェイスを定義するには、次の例に示すように、interface キーワードを使用します。

interface IEquatable<T>
{
    bool Equals(T obj);
}

インターフェイスの名前を、有効な C# の識別子名にする必要があります。 慣例により、インターフェイス名は大文字の I で始めます。

IEquatable<T> インターフェイスを実装するすべてのクラスまたは構造体は、インターフェイスで指定されたシグネチャに一致する Equals メソッドの定義を含む必要があります。 したがって、T を実装する IEquatable<T> 型のクラスが Equals メソッドを含むと想定したうえで、これを使用しこのてクラスの 1 つのインスタンスが同じクラスの別のインスタンスと等しいかどうかを判定できます。

IEquatable<T> の定義は Equals の実装を提供しません。 クラスまたは構造体には複数のインターフェイスを実装できます。ただし、クラスは 1 つのクラスからのみ継承できます。

抽象クラスの詳細については、「抽象クラスとシール クラス、およびクラス メンバー」を参照してください。

インターフェイスには、instance メソッド、プロパティ、イベント、インデクサー、またはこれらの 4 種類のメンバーの任意の組み合わせを含めることができます。 インターフェイスには、静的コンストラクター、フィールド、定数、または演算子を含めることができます。 フィールドではないインターフェイス メンバーを static abstractできます。 インターフェイスには、インスタンス フィールド、インスタンス コンストラクター、またはファイナライザーを含めることができません。 インターフェイス メンバーは、既定ではパブリックであり、publicprotectedinternalprivateprotected internalprivate protected などのアクセシビリティ修飾子を明示的に指定できます。 private メンバーには既定の実装が必要です。

暗黙的な実装を使用してインターフェイス メンバーを実装するには、実装クラスの対応するメンバーがパブリックで非静的であり、インターフェイス メンバーと同じ名前とシグネチャを持つ必要があります。 明示的なインターフェイス実装を使用して、パブリックではないインターフェイス メンバーを実装する必要があります。

注意

インターフェイスが静的メンバーを宣言する場合、そのインターフェイスを実装する型では、同じシグネチャを持つ静的メンバーを宣言することもできます。 これらのメンバーは、メンバーを宣言する型によって個別に一意に識別されます。 型で宣言された静的メンバーは、インターフェイスで宣言された静的メンバーをオーバーライド "しません"。

インターフェイスを実装するクラスまたは構造体は、インターフェイスに既定の実装が用意されていない場合に、宣言したすべてのメンバーの実装を提供する必要があります。 ただし、基底クラスがインターフェイスを実装する場合、基底クラスから派生したクラスは、その実装を継承します。

IEquatable<T> インターフェイスを実装する例を次に示します。 実装するクラスの Car は、Equals メソッドの実装を提供する必要があります。

public class Car : IEquatable<Car>
{
    public string? Make { get; set; }
    public string? Model { get; set; }
    public string? Year { get; set; }

    // Implementation of IEquatable<T> interface
    public bool Equals(Car? car)
    {
        return (this.Make, this.Model, this.Year) ==
            (car?.Make, car?.Model, car?.Year);
    }
}

クラスのプロパティとインデクサーは、インターフェイスで宣言されたプロパティまたはインデクサーの追加アクセサーを定義できます。 たとえば、インターフェイスで get アクセサーを持つプロパティを宣言するとします。 このインターフェイスを実装するクラスでは、get アクセサーと get アクセサーの両方を持つ同じプロパティを宣言できます。 ただし、プロパティまたはインデクサーで明示的な実装を使用する場合は、これらのアクセサーが一致する必要があります。 明示的な実装の詳細については、「明示的なインターフェイス実装」および「インターフェイスのプロパティ」を参照してください。

インターフェイスは、1 つ以上のインターフェイスから継承できます。 派生インターフェイスは、その基本インターフェイスからメンバーを継承します。 派生インターフェイスを実装するクラスでは、派生インターフェイスの基底インターフェイスのすべてのメンバーを含め、派生インターフェイスのすべてのメンバーを実装する必要があります。 そのクラスは、派生インターフェイスまたはその基本インターフェイスのいずれかに暗黙的に変換される場合があります。 クラスは、継承する基底クラス、または他のインターフェイスが継承するインターフェイスを介して、インターフェイスを複数回含めることができます。 ただし、クラスでインターフェイスの実装を提供できるのは 1 回のみであり、それもクラスでクラスの定義の一部としてインターフェイスを宣言する場合 (class ClassName : InterfaceName) に限られます。 インターフェイスを実装する基本クラスを継承することによってインターフェイスを継承する場合、基本クラスは、そのインターフェイスのメンバーの実装を提供します。 ただし、派生クラスでは、継承された実装を使用する代わりに、任意の仮想インターフェイスのメンバーを再実装できます。 インターフェイスにメソッドの既定の実装が宣言されている場合、そのインターフェイスを実装しているクラスにはその実装が継承されます (インターフェイス メンバーの既定の実装にアクセスするには、クラスのインスタンスをインターフェイス型にキャストする必要があります)。

また、基本クラスでは、仮想メンバーを使用して、インターフェイスのメンバーを実装することもできます。 その場合、派生クラスでは、仮想メンバーをオーバーライドすることでインターフェイスの動作を変更できます。 仮想メンバーの詳細については、「ポリモーフィズム」を参照してください。

内部インターフェイスの操作

内部インターフェイスは、通常、インターフェイス シグネチャ内のすべての型にパブリックにアクセスできる限り、パブリック メンバーとの暗黙的な実装を使用して実装できます。 ただし、インターフェイスがメンバーシグネチャで内部型を使用する場合、実装するクラス メンバーは内部型を公開している間にパブリックである必要があるため、暗黙的な実装は不可能になります。 このような場合は、明示的なインターフェイス実装を使用する必要があります。

次の例は、両方のシナリオを示しています。

// Internal type that cannot be exposed publicly
internal class InternalConfiguration
{
    public string Setting { get; set; } = "";
}

// Internal interface that CAN be implemented with public members
// because it only uses public types in its signature
internal interface ILoggable
{
    void Log(string message); // string is public, so this works with implicit implementation
}

// Interface with internal accessibility using internal types
internal interface IConfigurable
{
    void Configure(InternalConfiguration config); // Internal type prevents implicit implementation
}

// This class shows both implicit and explicit interface implementation
public class ServiceImplementation : ILoggable, IConfigurable
{
    // Implicit implementation works for ILoggable because string is public
    public void Log(string message)
    {
        Console.WriteLine($"Log: {message}");
    }

    // Explicit implementation required for IConfigurable because it uses internal types
    void IConfigurable.Configure(InternalConfiguration config)
    {
        // Implementation here
        Console.WriteLine($"Configured with: {config.Setting}");
    }
    
    // If we tried implicit implementation for IConfigurable, this wouldn't compile:
    // public void Configure(InternalConfiguration config) // Error: cannot expose internal type
}

前の例では、 IConfigurable インターフェイスはメソッド シグネチャで内部型 InternalConfiguration を使用します。 ServiceImplementation クラスでは暗黙的な実装を使用できません。Configure メソッドをパブリックにする必要があるためです。これは、メソッド シグネチャに内部型が含まれている場合は許可されません。 代わりに、明示的なインターフェイス実装が使用されます。これはアクセス修飾子を持たず、インターフェイス型を介してのみアクセスできます。

これに対し、 ILoggable インターフェイスは、インターフェイス自体が内部であっても、そのシグネチャ (string) 内のすべての型にパブリックにアクセスできるため、パブリック メンバーと共に暗黙的に実装できます。

明示的なインターフェイスの実装の詳細については、「 明示的なインターフェイスの実装」を参照してください。

インターフェイスの概要

インターフェイスは、次の特性を持ちます。

  • 8\.0 よりも前のバージョンの C# では、インターフェイスは抽象メンバーのみを含む抽象基本クラスに似ています。 インターフェイスを実装するクラスまたは構造体では、そのすべてのメンバーを実装する必要があります。
  • C# 8.0 以降では、インターフェイスはメンバーの一部またはすべての既定の実装を定義できます。 インターフェイスを実装するクラスまたは構造体では、既定の実装を持つメンバーを実装する必要はありません。 詳細については、既定のインターフェイス メソッドに関する記事をご覧ください。
  • インターフェイスを直接インスタンス化することはできません。
  • クラスまたは構造体は、複数のインターフェイスを実装できます。 クラスは、基本クラスを継承する一方で、1 つまたは複数のインターフェイスを実装できます。