Unity & C# 学習教材

インターフェイスの明示的実装

問題:同じシグネチャのメンバーが衝突するとき

IFooIBar がどちらも 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(同じ実装が使われる)
1
2
A.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() が呼ばれる
1
2
IFoo.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"); }
}

まとめ


理解度チェック

  1. 明示的実装を A a = new A(); のインスタンスから直接呼ぼうとするとどうなりますか?
  2. 明示的実装にアクセス修飾子(public など)を書くとどうなりますか?
  3. IFooIBar が同名メンバーを持ち、それぞれ異なる動作にしたい場合、暗黙的実装と明示的実装のどちらを使いますか?
解答を見る
  1. コンパイルエラーになります。明示的実装はインターフェイス型変数を通じてのみ呼び出せます。
  2. コンパイルエラーになります。明示的実装にはアクセス修飾子を付けられません。
  3. 明示的実装を使います。暗黙的実装では 1 つのメソッドが両方に使われるため区別できません。

次のステップ

ジェネリクスの基本 では、型パラメータ <T> を使って型安全な汎用クラスを定義するジェネリクスのしくみを学びます。