インターフェイスの明示的実装
問題:同じシグネチャのメンバーが衝突するとき
IFoo と IBar がどちらも void M() を持つとき、class A : IFoo, IBar はどう実装するか?
1
2
3
4
5
6
7
8
9
| interface IFoo
{
void M();
}
interface IBar
{
void M();
}
|
通常どおり public void M() を 1 つ書くと、両方のインターフェイスに同じ実装が使われます。これを暗黙的実装と呼びます。
1
2
3
4
| class A : IFoo, IBar
{
public void M() { Console.WriteLine("A.M"); }
}
|
1
2
3
4
5
| IFoo x = new A();
x.M(); // A.M
IBar y = new A();
y.M(); // A.M(同じ実装が使われる)
|
IFoo.M() と IBar.M() に別々の実装を持たせたい場合、明示的実装を使います。
学習目標
- 明示的実装の書き方を理解できる
- 明示的実装はインターフェイス型変数を通じてのみ呼べることを確認できる
- 暗黙的実装と明示的実装の動作の違いを説明できる
前提知識
1. 明示的実装の書き方
書式:明示的実装
1
2
3
| 戻り値の型 インターフェイス名.メソッド名(引数リスト)
{
}
|
| 要素 |
説明 |
| アクセス修飾子 |
書かない(書くとコンパイルエラー) |
インターフェイス名.メソッド名 |
どのインターフェイスのメンバーを実装するかを明示する |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| interface IFoo
{
void M();
}
interface IBar
{
void M();
}
class A : IFoo, IBar
{
void IFoo.M() { Console.WriteLine("IFoo.M"); }
void IBar.M() { Console.WriteLine("IBar.M"); }
}
|
2. 呼び出し方
明示的実装のメンバーは、インターフェイス型の変数を通じてのみ呼べます。クラスのインスタンスから直接呼ぶことはできません。
1
2
3
4
5
6
7
8
9
| A a = new A();
// ❌ コンパイルエラー: 明示的実装は直接呼べない
// a.M();
IFoo x = a;
x.M(); // IFoo.M() が呼ばれる
IBar y = a;
y.M(); // IBar.M() が呼ばれる
|
同じインスタンスでも、どのインターフェイス型変数を通じるかによって呼ばれるメソッドが変わります。
3. 暗黙的実装と明示的実装の比較
| 項目 |
暗黙的実装 |
明示的実装 |
| 書き方 |
public void M() { } |
void IFoo.M() { } |
| アクセス修飾子 |
public が必要 |
書けない |
| インスタンスから直接呼べるか |
呼べる |
呼べない |
| インターフェイス型変数経由 |
呼べる |
呼べる |
| 複数インターフェイスの同名メンバーを区別できるか |
できない(同一実装になる) |
できる |
よくあるミス
1
2
3
4
5
6
7
8
9
| class A : IFoo, IBar
{
// ❌ NG: 明示的実装にアクセス修飾子は書けない
// public void IFoo.M() { }
// ✅ OK: アクセス修飾子なし
void IFoo.M() { Console.WriteLine("IFoo.M"); }
void IBar.M() { Console.WriteLine("IBar.M"); }
}
|
まとめ
- 明示的実装は
void IFoo.M() { } の形で書く。アクセス修飾子は付けない
- インターフェイス型変数を通じてのみ呼び出せる
- 異なるインターフェイスに同名メンバーがあるときに、それぞれ別の実装を定義できる
理解度チェック
- 明示的実装を
A a = new A(); のインスタンスから直接呼ぼうとするとどうなりますか?
- 明示的実装にアクセス修飾子(
public など)を書くとどうなりますか?
IFoo と IBar が同名メンバーを持ち、それぞれ異なる動作にしたい場合、暗黙的実装と明示的実装のどちらを使いますか?
解答を見る
- コンパイルエラーになります。明示的実装はインターフェイス型変数を通じてのみ呼び出せます。
- コンパイルエラーになります。明示的実装にはアクセス修飾子を付けられません。
- 明示的実装を使います。暗黙的実装では 1 つのメソッドが両方に使われるため区別できません。
次のステップ
ジェネリクスの基本 では、型パラメータ <T> を使って型安全な汎用クラスを定義するジェネリクスのしくみを学びます。