C#のインターフェース:堅牢なオブジェクト指向設計のための契約定義
インターフェースとは、インターフェースを継承するすべてのクラスが従うべき構文上の契約として定義されます。インターフェースは構文契約の「何(What)」の部分を定義し、派生クラスが「どう(How)」の部分を定義します。
インターフェースはプロパティ、メソッド、イベントを定義し、これらはインターフェースのメンバーとなります。インターフェースにはメンバーの宣言のみが含まれます。メンバーを定義するのは派生クラスの責任です。これにより、派生クラスが従うべき標準的な構造を提供することがよくあります。
抽象クラスも一定程度同じ目的を果たしますが、基底クラスでわずかなメソッドのみを宣言し、派生クラスで機能を実装する場合に主に使用されます。
インターフェースの宣言
インターフェースはinterfaceキーワードで宣言します。クラス宣言に似ています。インターフェースのステートメントはデフォルトでpublicです。
public interface ITransactions {
// インターフェースメンバー
void ShowTransaction();
double GetAmount();
}
慣習として、インターフェース名にはIプレフィックスを付けます(IComparable, IDisposableなど)。
インターフェースの実装例
using System;
namespace InterfaceApplication {
public interface ITransactions {
void ShowTransaction();
double GetAmount();
}
public class Transaction : ITransactions {
private string tCode;
private string date;
private double amount;
public Transaction() {
tCode = " "; date = " "; amount = 0.0;
}
public Transaction(string c, string d, double a) {
tCode = c; date = d; amount = a;
}
public double GetAmount() => amount;
public void ShowTransaction() {
Console.WriteLine("Transaction: {0}", tCode);
Console.WriteLine("Date: {0}", date);
Console.WriteLine("Amount: {0}", GetAmount());
}
}
class Tester {
static void Main(string[] args) {
Transaction t1 = new Transaction("001", "8/10/2012", 78900.00);
Transaction t2 = new Transaction("002", "9/10/2012", 451900.00);
t1.ShowTransaction();
t2.ShowTransaction();
Console.ReadKey();
}
}
}
実行結果:
Transaction: 001
Date: 8/10/2012
Amount: 78900
Transaction: 002
Date: 9/10/2012
Amount: 451900
インターフェースの重要な特徴
- インターフェースには実装を含められない(C# 8.0以前)。メソッド本体、フィールド、コンストラクタ、デストラクタ不可
- すべてのメンバーは暗黙的に
public(アクセス修飾子の指定不要・不可) - クラスは複数のインターフェースを実装可能(多重継承の代替)
- インターフェースは他のインターフェースを継承可能
- 構造体(
struct)もインターフェースを実装可能
明示的なインターフェース実装
同じシグネチャのメンバーを持つ複数のインターフェースを実装する場合、またはクラスの公開APIから隠蔽したい場合に使用します。
interface IFirst { void Method(); }
interface ISecond { void Method(); }
class MyClass : IFirst, ISecond {
// 明示的実装(インターフェース名.メンバー名)
void IFirst.Method() { Console.WriteLine("IFirst.Method"); }
void ISecond.Method() { Console.WriteLine("ISecond.Method"); }
// 通常の公開メソッドとしても定義可能
public void Method() { Console.WriteLine("Public Method"); }
}
// 使用
MyClass obj = new MyClass();
obj.Method(); // "Public Method"
((IFirst)obj).Method(); // "IFirst.Method"
((ISecond)obj).Method(); // "ISecond.Method"
デフォルト実装(C# 8.0以降)
C# 8.0から、インターフェースにメソッドのデフォルト実装を提供できます。
interface ILogger {
void Log(string message);
// デフォルト実装
void LogError(string error) {
Log($"ERROR: {error}");
}
// 静的メソッドも可能
static void LogStatic(string msg) => Console.WriteLine($"STATIC: {msg}");
}
インターフェース vs 抽象クラス
| 特徴 | インターフェース | 抽象クラス |
|---|---|---|
| 多重実装 | 可能(複数実装可) | 不可(単一継承のみ) |
| 実装の有無 | 原則宣言のみ(C# 8.0+でデフォルト実装可) | 実装を持てる |
| フィールド | 持てない(定数のみ) | 持てる |
| コンストラクタ | 不可 | 可能 |
| アクセス修飾子 | すべてpublic(C# 8.0+でprivate static等可) | 自由 |
| 用途 | 「できること」の契約(CAN-DO) | 「であること」の共有(IS-A) |
よく使用される標準インターフェース
| インターフェース | 目的 | 主なメンバー |
|---|---|---|
IDisposable | リソース解放 | Dispose() |
IComparable<T> | 並べ替え比較 | CompareTo(T) |
IEquatable<T> | 値の等価性 | Equals(T) |
IEnumerable<T> | 列挙/反復 | GetEnumerator() |
ICloneable | 複製 | Clone() |
IAsyncDisposable | 非同期リソース解放 | DisposeAsync() |
依存性逆転の原則(DIP)とインターフェース
高レベルモジュールは低レベルモジュールに依存すべきではなく、両方とも抽象(インターフェース)に依存すべきです。
// 悪い例:具象クラスに依存
class UserService {
private SqlRepository repo = new SqlRepository(); // 具象依存
}
// 良い例:インターフェースに依存
class UserService {
private readonly IUserRepository repo; // 抽象依存
public UserService(IUserRepository repo) {
this.repo = repo; // DIコンテナから注入
}
}
ベストプラクティス
- インターフェースは小さく、単一責任に保つ(インターフェース分離の原則)
- 公開APIとして公開するメンバーのみをインターフェースに定義する
- 実装の詳細(内部ヘルパーメソッド等)をインターフェースに含めない
- 命名規則:
Iプレフィックス + 能力を表す形容詞または名詞(ISerializable,IRepository) - デフォルト実装(C# 8.0+)は既存実装を壊さずに機能追加する場合に活用
- テスタビリティ向上のため、依存関係はインターフェース経由で注入(DI)する
C言語
- 長い driveway 向けの信頼性の高いワイヤレスセンサー - Raspberry Piとの簡単統合
- SPICEでのコマンドラインインターフェースの使い方
- C#インターフェイスをマスターする:定義、実装、実践的なユースケース
- C++インターフェース完全理解:抽象クラスと純粋仮想関数
- Javaインターフェース完全ガイド:概念、実装、ベストプラクティス
- Javaコレクションフレームワーク入門:主要インターフェースと実装クラス
- Java Collectionインターフェース:核心概念と必須メソッド
- Java NavigableSetインターフェースをマスター:ナビゲーション、ソート、実践例
- Javaラムダ式完全ガイド:基礎からStream APIまで
- Javaインターフェース完全ガイド: 定義・実装・多重継承・タグインターフェース
- Java 9プライベートインターフェースメソッド:カプセル化とコード再利用を強化