明示的なインターフェイスの実装の使いどころ

C# には「明示的なインターフェースの実装」という言語仕様があります。

使い方

使い方はシンプルに以下の通り。

同じメソッドを持つ複数のインターフェースの実装を別々に実装することができます。

// 同じ名前を持つ2つのインターフェース
public interface IService1
{
    void Foo();
}
public interface IService2
{
    void Foo();
}
// インターフェースごとに別々の実装ができる
public class Service : IService1, IService2
{
    void IService1.Foo()
    {
        Console.WriteLine("IService1.Foo");
    }
    void IService2.Foo()
    {
        Console.WriteLine("IService2.Foo");
    }
    public void Foo()
    {
        IService1 s1 = this; // 自クラス内でもキャストが必要
        s1.Foo();
    }
}

// 使い方
public static Sample(Service s)
{
    // 具象クラスからは呼び出せない
    s.Foo();
    // エラー CS1061 'Service' に 'Foo' の定義が含まれておらず型 'Service' の
    // 最初の引数を受け付けるアクセス可能な拡張メソッド 'Foo' が見つかりませんでした。

    // インターフェース経由ならアクセスできる
    IService1 s1 = s;
    s1.Foo();
    // > IService1.Foo

    IService2 s2 = s;
    s2.Foo();
    // > IService2.Foo
}

この実装をした場合、具象クラスからはアクセスできなくなり、インターフェースにキャストしないと使用することができなくなります。

仕様的にはこれだけです。

実際どう使うか

現実的に、自分のコードで複数のインターフェースのメソッド名が重複し、更に各々実装を分けたい場合、メソッドの名前を変えたほうがましです。他人の作った(自分では変更不能な)ライブラリの実装からインターフェースを複数継承する場合ごく稀にこういうケースがあるかもしれません。これが通常の使い方です。

もうひとつの使い方は「インターフェースにキャストしないと呼び出せなくなる」という性質を利用した実装の不可視化です。

例えばこんな感じでイベントハンドラーを追加するインターフェースがあったとします。

// イベントハンドラを付加するインターフェース
public interface IPointerDownHandler
{
    void OnPointerDown(PointerEventData e);
}
// 実装クラス
public class MyClass : IPointerDownHandler
{
    void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
    {
        throw new NotImplementedException();
    }
}

この時、

  • (1) クラスの利用者はこのメソッドに簡単にアクセスしてほしくない
  • (2) 自分もこのメソッドを直接呼ぶことがまず無い

という条件を満たしたとき実装を隠す目的でも使用できます。自作ライブラリのフレームワークからしか呼ばれない都合で付与しているとかで、安易に呼び出されると少し困る、みたいな場合には使用できそうです。

ただ、他人の都合で仕方なくこうするケース以外でこの仕様を使って実装を隠すような設計は、設計自体に問題がある可能性が高そうなので、これを使いたいなと思ったときは少し考えなおしたほうがいいかもしれません。