C# におけるインターフェースと抽象クラスの使い分けガイド

本資料では、C# 8.0 以降の機能拡張を踏まえ、インターフェースと抽象クラスの特徴、各々の利用方法、及び使い分けについて解説します。
また、インターフェースがメソッドだけでなくプロパティも持てる点についても説明します。


1. インターフェースの概要

1.1 基本的な定義

インターフェースは、クラスや構造体が実装すべき「何ができるか(What)」を定義するための契約です。
従来、インターフェースは実装を持たず、すべての実装クラスで必ずオーバーライドする必要がありました。

public interface IExample
{
    void Method1(); // 実装なし(従来の定義)
}

1.2 デフォルト実装付きのメソッド(C# 8.0 以降)

C# 8.0 からは、インターフェースにデフォルト実装付きのメソッドを定義できるようになりました。
これにより、既存のインターフェースを拡張して新たなメソッドを追加しても、既存の実装クラスに影響を与えずに利用できます。

public interface IExample
{
    void Method1(); // 従来のメソッド定義

    // デフォルト実装付きのメソッド
    void Method2()
    {
        Console.WriteLine("Default implementation of Method2");
    }
}

実装クラスが Method2 をオーバーライドしなければ、デフォルト実装がそのまま使用されます。

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

インターフェースは、メソッドだけでなくプロパティも定義できます。

1.3.1 従来のプロパティ定義(C# 7.0 以前)

C# 7.0 以前は、プロパティのシグネチャ(getter/setter の定義)のみを記述し、実装クラスで必ず実装する必要がありました。

public interface IPerson
{
    string Name { get; set; }  // 実装なしのプロパティ定義
}

1.3.2 デフォルト実装付きプロパティ(C# 8.0 以降)

C# 8.0 以降では、プロパティにもデフォルトの実装を提供できます。
実装クラスは、必ずしもそのプロパティをオーバーライドする必要がなくなります。

public interface IPerson
{
    string Name { get; set; }  // 従来の定義

    // デフォルト実装付きのプロパティ
    int Age
    {
        get => 25;
        set => Console.WriteLine("Age is set to: " + value);
    }
}

1.3.3 静的プロパティ

また、C# 8.0 以降、静的プロパティもインターフェースに定義可能です。
静的プロパティは実装クラスではオーバーライドできず、直接インターフェースからアクセスします。

public interface IConfig
{
    static string DefaultLanguage { get; set; } = "English";
}

2. 抽象クラスの概要

2.1 基本的な定義

抽象クラスは、インターフェースと異なり「どのように動作するか(How)」を具体的に定義できるクラスです。
抽象クラスは、以下の特徴を持ちます。

  • コンストラクタフィールドプロパティを持つことができる
  • アクセス修飾子(privateprotected など)を自由に設定可能
  • 単一継承のみ(C# はクラスの多重継承をサポートしていない)

例: 抽象クラスの定義

public abstract class Vehicle
{
    public int Speed { get; set; } // 共通のプロパティ

    public abstract void Move(); // 抽象メソッド(派生クラスで必ず実装する必要がある)

    public void Stop() // 具象メソッド
    {
        Console.WriteLine("Vehicle stopped.");
    }
}

2.2 抽象クラスの特徴

  • 共通のデータ(フィールドやプロパティ)の保持が可能
  • 共通の動作(具象メソッド)の実装ができる
  • コンストラクタを使用して初期化処理が可能

3. インターフェースと抽象クラスの使い分け

3.1 比較表

特徴インターフェース抽象クラス
目的クラスの「何ができるか(What)」を定義クラスの「基本動作(How)」を定義
継承多重継承が可能単一継承のみ
デフォルト実装C# 8.0 以降、メソッドやプロパティにデフォルト実装が可能すべてのメソッドに具体的な実装が可能
フィールドの保持フィールドは持てないフィールドを保持可能
コンストラクタ持てないコンストラクタを持つことができる
アクセス修飾子すべて public(明示的な指定はできない)privateprotected など、柔軟に設定可能
使用場面異なる種類のクラスに共通の「能力」を提供したい場合、多重継承が必要な場合共通のデータや基本動作をまとめる場合

3.2 適した使い分けの例

  • インターフェース
    • 複数の異なるクラスに共通の機能や能力を付与する場合(例: IVehicleIFlyableIDisposable
    • 多重継承が必要な場合
  • 抽象クラス
    • 共通のフィールド、プロパティ、または初期化処理を持たせたい場合(例: Vehicle クラスを基底に CarBike を実装する場合)
    • 基本的な動作(How)の実装を共通化したい場合

4. 結論

  • C# 8.0 以降、インターフェースはデフォルト実装付きのメソッドやプロパティを定義できるようになり、設計の柔軟性が向上しました。
  • インターフェースは「何ができるか」を定義し、異なる種類のクラス間で共通の能力を付与するのに適しています。
  • 抽象クラスは「どのように動作するか」を定義し、共通のデータや動作の実装をまとめるのに適しています。
  • インターフェースはフィールドコンストラクタを持つことができないため、状態管理や初期化が必要な場合は抽象クラスの利用が推奨されます。

各プロジェクトの要件や設計思想に応じて、最適な選択を行ってください。


以上が、C# におけるインターフェースと抽象クラスの使い分け、およびインターフェースのプロパティ利用に関する概要です。