C#のインターフェース(interface)は、クラスや構造体が実装すべきメンバーの仕様だけを定義し、実際の処理内容は持たない「契約書」のようなものです。複数の型で共通の動作を保証できる一方、実装の詳細には関与しないため、疎結合で拡張性やテストの容易性を高められます。本記事ではインターフェースの基本から応用までを丁寧に解説します。※難しい箇所は読み飛ばしてもらって大丈夫です!
インターフェース(interface)の定義
C#のインターフェースはinterfaceキーワードで定義します。メソッド、プロパティ、イベント、インデクサーなどを持てますが、実装は一切含みません。
public interface ILogger
{
    void LogInfo(string message);
    void LogError(string message, Exception ex);
}
上記の例ではILoggerがログ出力機能を表すinterface(=契約)です。このinterfaceを実装するクラスは必ずLogInfoとLogErrorメソッドを定義しなければなりません。
なぜインターフェースを使うのか
- 依存性の逆転
高水準モジュール(主要なビジネスロジック)が低水準モジュール(具体的な実装)に直接依存せず、抽象化されたインターフェースに依存することで、変更に強い設計を実現します。 - テスト容易性
テスト用のスタブやモック(代替品)をインターフェース型で差し替えやすく、ユニットテストの効率が上がります。 - 多重継承の代替
C#はクラスの多重継承をサポートしませんが、複数のインターフェースを実装可能なため、機能の組み合わせを柔軟に行えます。
※クラスの継承では1つの形しか継承できないが、インターフェースは1つのクラスに複数使用できるため機能を組み合わせることができる。 
インターフェースの宣言と実装
以下はILoggerを実装する具体例です。
public class ConsoleLogger : ILogger
{
    public void LogInfo(string message)
    {
        Console.WriteLine($"INFO: {message}");
    }
    public void LogError(string message, Exception ex)
    {
        Console.WriteLine($"ERROR: {message} - {ex.Message}");
    }
}
ConsoleLoggerはILoggerを実装しているため、ILogger型として扱うことができます。
以下の例ではServiceのコンストラクタでILogger型を受け取って自身のロガーに設定しています。ここで、Serviceのコンストラクタに上記のConsoleLoggerを渡すことでServiceのロガーとしてConsoleLoggerを使用させることが可能です。
public class Service
{
    private readonly ILogger _logger;
    public Service(ILogger logger)
    {
        _logger = logger;
    }
    public void Execute()
    {
        _logger.LogInfo("処理開始");
        // 処理内容
        _logger.LogInfo("処理終了");
    }
}
インターフェースのメリット
- 実装の隠蔽と抽象化を同時に実現
 - 拡張やリファクタリングの影響範囲を最小化
 - DI(依存性注入)フレームワークとの親和性が高い
 - 複数の実装を切り替えて動作確認や機能追加が容易
 
インターフェースの活用例
- プラグイン設計
インターフェースでプラグイン仕様を定め、アプリ起動時にアセンブリから実装クラスを動的に読み込む。 - リポジトリパターン
DBなどのデータアクセスをIRepositoryで抽象化し、SQL、NoSQL、モックの切り替えを自在に行う。 - イベントハンドリング
IObserver/IObservableインターフェースを使ったリアクティブプログラミング。 
インターフェースで入出力の仕様を定めておくことで内部の実装は柔軟に行うことが可能です。
インターフェース vs 抽象クラス
インターフェースと似た実装として抽象クラスがあります。
| 項目 | インターフェース | 抽象クラス | 
|---|---|---|
| 実装の有無 | 実装を持たない(C#8.0以降はデフォルト実装可) | メソッドの実装を持てる | 
| 継承 | 多重実装可能 | 単一継承のみ | 
| 用途 | 機能の仕様定義 | 基本動作と共通フィールドを提供 | 
| バージョン互換維持 | メンバー追加は非互換 | 保守的にメンバー追加が可能 | 
C# 8.0以降のインターフェース拡張
C# 8.0からはインターフェースにデフォルト実装が書けるようになりました。後方互換性を壊さずに新メソッドを追加できます。
public interface IGreeter
{
    void SayHello(string name);
    void SayGoodbye(string name)
    {
        Console.WriteLine($"Goodbye, {name}!");
    }
}
デフォルト実装を使わない場合は従来どおり具象クラスでオーバーライドが可能です。
インターフェースのベストプラクティス
- インターフェース名は「I + 名詞/動詞」で命名(例:
IRepository、ICalculator) - 単一責任の原則に沿って粒度を細かく設計
※「単一責任の原則」:クラスやモジュールは「一つの理由」で変更されるべき、という原則です。つまり、1つのクラスやモジュールは、1つの責任(機能、変更されるべき理由)だけを持つべきということです。 - メソッド数が増えすぎないように注意
 - モック/スタブを用いてテストを容易に作成できるようにすべき
 - DIコンテナとの組み合わせでライフサイクル管理を統一
 
まとめ
C#のインターフェースは、設計の柔軟性やメンテナンス性を飛躍的に高める強力な機能です。実装の詳細を切り離し、契約として扱うことで依存性を低減し、テスト容易性や拡張性を担保できます。プラグインやリポジトリパターンなど、多様な場面で活用し、堅牢で読みやすいコードを実現しましょう!

  
  
  
  
