インターフェイスの実装で (インターフェイス名).(メソッド名) の記述で実装されているメソッドがある - 明示的なインターフェイスメンバーの実装 - C#

インターフェイスの実装で (インターフェイス名).(メソッド名) の記述で実装されているメソッドがあるコードについての紹介です。

概要

インターフェイスを実装するクラスで、(インターフェイス名).(メソッド名) で記述されているコードがあります。
具体例として次のコードを挙げます。
  internal class MyClass : IMyInterface
  {
    public int Proc1()
    {
      return 100;
    }

    int IMyInterface.Proc2()
    {
      return 200;
    }
  }

上記のコードの int IMyInterface.Proc2() 部分は「明示的なインターフェイスメンバーの実装」と呼ばれるコードです。
この書式を利用すると、インタ―フェイスを多重継承した場合に同名のメソッドがあった場合、どちらのインターフェイスの実装かを明示的に指定できます。 また、インターフェイスのスコープが狭くなり、実装クラスからアクセスできなくなるため、メソッドをインターフェイスを除外することができます。
以下、サンプルコードを例に動作を確認します。

プログラム例1:通常のインターフェイス実装

最初に通常のインターフェイス実装コードの動作を確認します。

UI

下図のUIを作成します。ボタンとテキストボックスを配置します。

コード

IMyInterface1.cs
namespace InterfaceExplicitImplementation
{
  internal interface IMyInterface1
  {
    public int Proc1();
    public int Proc2();
  }
}

MyClass1.cs
namespace InterfaceExplicitImplementation
{
  internal class MyClass1:IMyInterface1
  {
    public int Proc1()
    {
      return 10;
    }

    public int Proc2()
    {
      return 20;
    }

  }
}
FormSimpleExplicitImplementation.cs
namespace InterfaceExplicitImplementation
{
  public partial class FormSimpleExplicitImplementation : Form
  {
    public FormSimpleExplicitImplementation()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      MyClass1 mc1 = new MyClass1();
      textBox1.Text += String.Format("value Proc1:{0:d}\r\n", mc1.Proc1());
      textBox1.Text += String.Format("value Proc2:{0:d}\r\n", mc1.Proc2());
    }
  }
}

解説

IMyInterface1 インターフェイスを用意します。2つのメソッド Proc1() Proc2() メソッドを定義します。
MyClass1 クラスは、IMyInterface1 インターフェイスを継承し、2つのメソッドの実装を記述します。
フォームではボタンのクリックにより、MyClass1 のインスタンスを作成し、Proc1, Proc2 メソッドを呼び出し、メソッドの戻り値をテキストボックスに表示します。
    private void button1_Click(object sender, EventArgs e)
    {
      MyClass1 mc1 = new MyClass1();
      textBox1.Text += String.Format("value Proc1:{0:d}\r\n", mc1.Proc1());
      textBox1.Text += String.Format("value Proc2:{0:d}\r\n", mc1.Proc2());
    }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[button1]をクリックします。下図のメッセージがテキストボックスに表示されます。
Proc1, Proc2 メソッドの結果がテキストボックスに表示されます。

プログラム例2:明示的なインターフェイスメンバーの実装

UI


コード

MyClass2を作成し、以下のコードを追加します。
MyClass2.cs
namespace InterfaceExplicitImplementation
{
  internal class MyClass2 : IMyInterface1
  {
    public int Proc1()
    {
      return 10;
    }

    int IMyInterface1.Proc2()
    {
      return 20;
    }
  }
}

button2 のコードを実装します。
FormSimpleExplicitImplementation.cs
namespace InterfaceExplicitImplementation
{
  public partial class FormSimpleExplicitImplementation : Form
  {
    public FormSimpleExplicitImplementation()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      MyClass1 mc1 = new MyClass1();
      textBox1.Text += String.Format("value Proc1:{0:d}\r\n", mc1.Proc1());
      textBox1.Text += String.Format("value Proc2:{0:d}\r\n", mc1.Proc2());
    }

    private void button2_Click(object sender, EventArgs e)
    {
      MyClass2 mc2 = new MyClass2();
      textBox1.Text += String.Format("value Proc1:{0:d}\r\n", mc2.Proc1());
      textBox1.Text += String.Format("value Proc2:{0:d}\r\n", mc2.Proc2());
    }
  }
}

ビルド結果

上記のコードはコンパイルエラーになります。以下のエラーメッセージが表示されます。
エラーメッセージ
CS1061 : 'MyClass2' に 'Proc2' の定義が含まれておらず、型 'MyClass2' の最初の引数を受け付けるアクセス可能な拡張メソッド 'Proc2' が見つかりませんでした。 using ディレクティブまたはアセンブリ参照が不足していないことを確認してください

解説

int IMyInterface1.Proc2() の部分が明示的なインターフェイスメンバーの実装部分になります。 明示的なインターフェイスメンバーで実装した場合には、クラス(今回の例ではMyClass2)からはアクセスできません。 そのため、アクセス修飾子(public, private, protected など)は記述しません。
namespace InterfaceExplicitImplementation
{
  internal class MyClass2 : IMyInterface1
  {
    /*
      中略
    */

    int IMyInterface1.Proc2()
    {
      return 20;
    }
  }
}

明示的なインターフェイスメンバーの実装で、下記のコードのように、アクセス修飾子を記述するとコンパイルエラーになります。
エラーメッセージ
CS0106 : 修飾子 'public' がこの項目に対して有効ではありません
    public int IMyInterface1.Proc2()
    {
      return 20;
    }

修正

コンパイルが通るようにするためには下記コードに修正します。IMyInterface1 にキャストすると、Proc2メソッドを呼び出せます。
    private void button2_Click(object sender, EventArgs e)
    {
      MyClass2 mc2 = new MyClass2();
      textBox1.Text += String.Format("value Proc1:{0:d}\r\n", mc2.Proc1());
      textBox1.Text += String.Format("value Proc2:{0:d}\r\n", ((IMyInterface1)mc2).Proc2());
    }

実行結果

プロジェクトを実行しウィンドウの[button2]ボタンをクリックします。下図の結果が画面に表示されます。

実用例1: インターフェイスのメソッドを隠す

クラスの実装部で、Proc2 メソッドを明示的なインターフェイスメンバーの実装にすることで、MyIAClassクラスのProc2メソッドはアクセスできなくなります。 MyIAClass クラスから、Proc2メソッドを隠すことができます。
IMyIAInterface.cs
namespace InterfaceExplicitImplementation
{
  internal interface IMyInterface2
  {
    int Proc1();
    int Proc2();
  }
}
MyIAClass.cs
namespace InterfaceExplicitImplementation
{
  public class MyIAClass: IMyIAInterface
  {
    public int Proc1()
    {
      return 2;
    }

    int IMyIAInterface.Proc2()
    {
      return 4;
    }

  }
}

実用例2: インターフェイスの戻り値を変える

インターフェイスの実装クラスでインターフェイスの戻り値を変更したい場合に、インターフェイスの定義のメソッドを 明示的なインターフェイスメンバーの実装にして隠すことで、戻り値の型の違う同名のメソッドを実装できます。
下記のコード例では、元のProc1 メソッドを隠し、string型が返るProc1メソッドを MyIAClass に実装しています。
IMyIAInterface.cs
namespace InterfaceExplicitImplementation
{
  internal interface IMyInterface2
  {
    int Proc1();
    int Proc2();
  }
}
MyIAClass.cs
namespace InterfaceExplicitImplementation
{
  public class MyIAClass: IMyIAInterface
  {
    int IMyIAInterface.Proc1()
    {
      return 2;
    }

    public string Proc1()
    {
      return "ぺんぎんクッキー";
    }

    int IMyIAInterface.Proc2()
    {
      return 4;
    }

  }
}

FormInaccesssibleMethod.cs
namespace InterfaceExplicitImplementation
{
  public partial class FormInaccesssibleMethod : Form
  {
    public FormInaccesssibleMethod()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      MyIAClass mic = new MyIAClass();
      textBox1.Text += String.Format("value Proc1:{0}", mic.Proc1());
    }
  }
}

実行結果は下図となります。Proc1メソッドは文字列の値が戻りテキストボックスに表示される動作が確認できます。


補足:明示的なインターフェイスメンバーの実装を利用しない場合

なお、下記のコードで明示的なインターフェイスメンバーの実装を利用せずに同名のメソッドを実装した場合は、以下のエラーが発生します。
namespace InterfaceExplicitImplementation
{
  public class MyIAClass: IMyIAInterface
  {
    public int Proc1()
    {
      return 2;
    }

    public string Proc1()
    {
      return "ぺんぎんクッキー";
    }

    int IMyIAInterface.Proc2()
    {
      return 4;
    }

  }
}
エラーメッセージ
CS0111 : 型 'MyIAClass' は、'Proc1' と呼ばれるメンバーを同じパラメーターの型で既に定義しています


著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2022-05-26
iPentec all rights reserverd.