インターフェース

《 初回公開:2020/06/28 , 最終更新:未 》

visual studio2019 C#8.0および一部C#7.3で動作確認。

【 目次 】

インターフェイスというのは

インターフェイスという言葉にはいろいろな意味があって

  • インターフェース - Wikipedia

    インターフェース (英: interface) はインタフェース、インタフェイス、インターフェイスとも書き、英語で界面や接触面、中間面などといった意味を持ち、転じてコンピュータと周辺機器の接続部分を表すようになった。
    さらに、ユーザーインターフェースなどのように、人間と自動機械との間の複雑な操作をする手順・規則との意味にも使われる。
    インタフェース (情報技術) - ものごとの境界となる部分と、その境界での処理方式であるプロトコルを指す、主に情報技術関連で用いられる用語。

プログラミングにおけるインターフェースとは

そしてC#やJava等のオブジェクト指向プログラミング言語におけるインターフェイスとは

  • インタフェース (抽象型) - Wikipedia

    インタフェース (英: interface) は、JavaやC#などのオブジェクト指向プログラミング言語においてサポートされる、実装を持たない抽象型のことである。
    これらの言語において、クラスは実装の多重継承をサポートしない代わりに、任意の数のインタフェースを実装 (implement) することができ、これにより型の多重継承をサポートする。
    複数の種類のオブジェクトを、インタフェースを用いた多態性によって統一的に扱うことができるようになる。
    インターフェイスなどと表記することもある[1]

C#では

  • インターフェイス - C# プログラミング ガイド | Microsoft Docs

    インターフェイスの名前を、有効な C# の識別子名にする必要があります。 慣例により、インターフェイス名は大文字の I で始めます。
    ...
    インターフェイスには、instance メソッド、プロパティ、イベント、インデクサー、またはこれらの 4 種類のメンバーの任意の組み合わせを含めることができます。 インターフェイスには、静的コンストラクター、フィールド、定数、または演算子を含めることができます。 例へのリンクについては、「関連項目」を参照してください。 インターフェイスには、インスタンス フィールド、インスタンス コンストラクター、またはファイナライザーを含めることができません。 インターフェイス メンバーは、既定でパブリックです。
    ...
    インターフェイスでは、フィールド、自動実装プロパティ、プロパティに似たイベントなどのインスタンス データを宣言できません。
    ...
    インターフェイスには、インスタンス フィールド、インスタンス コンストラクター、またはファイナライザーを含めることができません。 インターフェイス メンバーは、既定でパブリックです。

C# 8.0 以降でインターフェイスの仕様もだいぶ拡張されたようで

  • インターフェイス - C# プログラミング ガイド | Microsoft Docs

    インターフェイスには、非抽象クラスまたは構造体で実装する必要がある、
    関連する機能のグループに対する定義が含まれます。 インターフェイスを使用すると、実装が必要な static メソッドを定義することができます。
    C# 8.0 以降では、インターフェイスによってメンバーの既定の実装を定義できます。
    インターフェイスでは、フィールド、自動実装プロパティ、プロパティに似たイベントなどのインスタンス データを宣言できません。

C#におけるインターフェイスとはクラスがある共通の機能を使うため必要なメソッド等を定めたものと言う事ができる。
例えば、クラスのオブジェクトが他のオブジェクトと比較できるようにするためには
具体的にはArrayListのSortメソッドを利用するにはArrayListの要素がIComparableインターフェースを備えていなければならないとか。
他にも、foreachループを利用するにはIEnumerableインターフェースを備えていなければならないとか。
そういうクラスがある機能を使うために備えなければならない規約を定めた設計書の役割を果たすものと考えられる。

インターフェイスは抽象クラスに似ている。
インターフェイスで定義されるメソッドも抽象メソッドのように見える。
しかし抽象クラスは多重継承できないがインターフェイスは一つのクラスに対して複数のインターフェイスを指定できるので多重継承の役割を果たす事ができると言える。

IComparableインターフェースとかIEnumerableインターフェースついては次の記事で述べるとして、簡単にインターフェースの例を示す事にする。

インターフェースを使ったコードを記述してみる

まず、自分自身のオブジェクトの内部変数の値をコンソールに表示するIShowrableインターフェースを定義し、

interface IShowrable
{
    void ShowMe();
}

このIShowrableインターフェースを実装したMyShowrableクラスとOtherShowrableクラスを定義する。

class MyShowrable : IShowrable
{
    private const string myFieldStr = "MyShowrableクラスのmyFieldStr変数";
    public void ShowMe() => Console.WriteLine(myFieldStr);
}
class OtherShowrable : IShowrable
{
    private const string otherFieldStr = "OtherShowrableクラスのotherFieldStr変数";
    public void ShowMe() => Console.WriteLine(otherFieldStr);
}

インターフェースのメンバーは通常その性質上、既定のpublicで使用されるためアクセス装飾子は指定しない。
メソッド内のコードも、インターフェースを実装したクラスで記述されるためインターフェースでは記述しない。

このIShowrableインターフェースを実装したクラスを要素に持つLIstクラスを定義して、各Listの要素のShowMeメソッドを呼び出すShowAllElementsメソッドを定義する。

class ShowrableList : List<IShowrable>
{
    public void ShowAllElements()
    {
        foreach(IShowrable showrable in this)
        {
            showrable.ShowMe();
        }
    }
}

このShowrableListを使って

var list = new ShowrableList();
list.Add(new MyShowrable());
list.Add(new OtherShowrable());

list.ShowAllElements();

MyShowrableクラスとOtherShowrableクラスは、IShowrableインターフェースを実装しているだけで他に関係は無い。
しかしIShowrableインターフェースを実装する事で、同じクラスであるかのように操作する事ができる。

実行結果

MyShowrableクラスのmyFieldStr変数
OtherShowrableクラスのotherFieldStr変数

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

インターフェースの明示的実装というのがあって、上記のMyShowrableクラスをインターフェースの明示的実装を使って記述すると

class MyShowrable : IShowrable
{
    void IShowrable.ShowMe() => Console.WriteLine("MyShowrableクラス");
}

ShowMeメソッドのにアクセス修飾子publicが付いていない事に注意。
明示的実装ではアクセス修飾子を付けられないようである。

また、通常のインターフェースのメソッド(暗黙的実装という言葉で良いのかな,今後は暗黙的実装と呼ぶ事にする)とは異なり
明示的実装によるメソッドはMyShowrableのインスタンスから直接呼び出す事はできないようで、次のコードはコンパイルエラーになる。

コンパイルエラーになる

var myShowrable = new MyShowrable();
myShowrable.ShowMe();

インターフェースをとおしてのアクセスのみが可能で、次のようなコードで呼び出す事ができる。

これならコンパイルエラーにならない

IShowrable myShowrable = new MyShowrable();
myShowrable.ShowMe();

また暗黙的実装とは異なり、次のように同名のクラスのメソッドとインターフェースのメソッドの両方を定義する事が可能で

class MyShowrable : IShowrable
{
    public void ShowMe() => Console.WriteLine("MyShowrableクラス");
    void IShowrable.ShowMe() => Console.WriteLine("IShowrableインターフェースのShowMeメソッドの明示的実装");
}

この時、以下のように同名のクラスのメソッドとインターフェースのメソッドを呼び分ける事ができる

var myShowrable = new MyShowrable();
myShowrable.ShowMe();
((IShowrable)myShowrable).ShowMe();

実行結果

MyShowrableクラス
IShowrableインターフェースのShowMeメソッドの明示的実装

インターフェースの明示的実装と複数のインターフェースを実装したクラス

複数のインターフェースを実装したクラスを定義できる。
C#は複数のクラスを継承するいわゆる多重継承をサポートしていない。
代わりに複数のインターフェースを実装する事で多重継承の複雑さを回避している。

明示的実装がどうゆう時に必要になるかというと、同名のメンバーを持つ異なる2つのインターフェースを実装する時で、明示的実装により同名のメソッドがどっちのインターフェースのメソッドなのかを区別する事ができる。

IShowrableインターフェースのShowMeメソッドと同名のメソッドを持つ、IMsgBoxShowrableインターフェースを定義する。

interface IMsgBoxShowrable
{
    void ShowMe();
}

このIMsgBoxShowrableインターフェースとIShowrableインターフェースを実装するMultiShowrableクラスを定義。

class MultiShowrable : IShowrable, IMsgBoxShowrable
{
    void IShowrable.ShowMe() => Console.WriteLine("MultiShowrableクラスIShowrableインターフェースのShowMeメソッドの明示的実装");
    void IMsgBoxShowrable.ShowMe() => MessageBox.Show("MultiShowrableクラスIMsgBoxShowrableインターフェースのShowMeメソッドの明示的実装");
}

MultiShowrableクラスのインスタンスを生成してIShowrableインターフェースのShowMeメソッドとIMsgBoxShowrableインターフェースのShowMeメソッドをそれぞれ実行してみる。

var multiShowrable = new MultiShowrable();
((IShowrable)multiShowrable).ShowMe();
((IMsgBoxShowrable)multiShowrable).ShowMe();

コンソールには「MultiShowrableクラスIShowrableインターフェースのShowMeメソッドの明示的実装」が出力されて、メッセージボックスには「MultiShowrableクラスIMsgBoxShowrableインターフェースのShowMeメソッドの明示的実装」が表示されるのが確認できる。

※.この記事での動作確認には.NET CoreのC#8.0で動作確認をしているが、現状.NET Coreではメッセージボックス等のGUIがサポートされていないようなので、上記のコードは.NET FrameworkのC#7.3で動作確認をしている。

クラスのインターフェース継承

では、IShowrableインターフェースを実装したクラスを継承したクラスにMsgBoxShowrableインターフェースを実装した場合に、スーパクラスで実装したIShowrableインターフェースのShowMeメソッドを呼び出す事はできるのだろうか?
ここでちょっと実験をしてみる。

前記のIShowrableインターフェースを実装したクラスMyShowrableを継承したMyShowrableExを定義して

class MyShowrableEx : MyShowrable  { }

スーパクラスMyShowrableとIShowrableインターフェースのShowMeShowMeメソッドを呼び出してみる。

var myShowrableEx = new MyShowrableEx();
myShowrableEx.ShowMe();
((IShowrable)myShowrableEx).ShowMe();

実行結果

MyShowrableクラス
MyShowrableクラスIShowrableインターフェースのShowMeメソッドの明示的実装

スーパクラスで実装したインターフェースのメソッドが子クラスでも呼び出す事ができるのがわかる。

明示的実装のインターフェースメソッドのオーバーライド

では子クラスで、スーパクラスのインターフェースで実装したメソッドの動作を変更するにはどうしたらいいだろう。
以下のように子クラスでIShowrableインターフェースのShowMeShowMeメソッドを上書きしようとしてもコンパイルエラーとなってしまう。

class MyShowrableEx2 : MyShowrable {
    // 下記のコードはエラー(C# 7.3で確認)
    void IShowrable.ShowMe() => Console.WriteLine("MyShowrableクラスIShowrableインターフェースのShowMeメソッドの明示的実装");
}

メソッドの上書きだからといってabstractやvirtualメソッドのようにoverrideキーワードでももちろんダメ。
そういう意味ではインターフェースのメソッドはクラスのabstractメソッドとは多少違うようだ。

// 下記のコードはエラー(C# 7.3で確認)
override void IShowrable.ShowMe() => Console.WriteLine("MyShowrableクラスIShowrableインターフェースのShowMeメソッドの明示的実装");

abstract,virtual,overrideについては

いろいろ試行錯誤した結果、子クラスで再度インターフェースを指定し直せば良いようだ。

class MyShowrableEx3 : MyShowrable, IShowrable
{
    void IShowrable.ShowMe()
    {
        Console.WriteLine("MyShowrableEx3クラスIShowrableインターフェースのShowMeメソッドの明示的実装");
        base.ShowMe();
        // 下記のコードはエラー(C# 7.3で確認)
        // ((IShowrable)base).ShowMe();
    }
}

MyShowrableEx3クラスのインスタンスを生成して上書きしたIShowrableインターフェースのShowMeメソッドを実行してみる。

var myShowrableEx3 = new MyShowrableEx3();
((IShowrable)myShowrableEx3).ShowMe();

実行結果

MyShowrableEx3クラスIShowrableインターフェースのShowMeメソッドの明示的実装
MyShowrableクラス

上書きしたメソッドが正しく呼び出されているのが確認できる。
このコードのように上書きしたメソッド内で明示的実装のインターフェースメソッドを呼び出す事はできないが、暗黙的に実装されたインターフェースメソッド(baseクラスのShowMeメソッド)を呼び出す事は可能なようだ。

インターフェースメソッドの暗黙的実装のメソッドの継承は

とここまではインターフェースのメソッドの明示的実装の場合であって、暗黙的実装ではメソッドにoverride修飾子が使用できて

基底クラスのインターフェースのメソッドにvirtual指定して定義,それを継承するクラスを定義

class MyShowrableBase : IShowrable
{
    public virtual void ShowMe() => Console.WriteLine("MyShowrableBaseクラスのShowMe");
}
class MyShowrableEx1 : MyShowrableBase
{
}
class MyShowrableEx2 : MyShowrableBase
{
    public override void ShowMe()
    {
        base.ShowMe();
        Console.WriteLine("MyShowrableExクラスのShowMe");
    }
}

各クラスのインターフェースメソッドを呼び出す。

// MyShowrableBaseクラスのShowMeメソッドを実行
var myShowrableBase = new MyShowrableBase();
((IShowrable)myShowrableBase).ShowMe();

// MyShowrableEx1よりmyShowrableBaseクラスのShowMeメソッドを実行
var myShowrableEx1 = new MyShowrableEx1();
((IShowrable)myShowrableEx1).ShowMe();

// MyShowrableEx2クラスのShowMeメソッドを実行
var myShowrableEx2 = new MyShowrableEx2();
((IShowrable)myShowrableEx2).ShowMe();

実行結果

MyShowrableBaseクラスのShowMe
MyShowrableBaseクラスのShowMe
MyShowrableBaseクラスのShowMe
MyShowrableExクラスのShowMe

abstractとして定義する場合は

abstract class AbstractShowrable : IShowrable
{
    public abstract void ShowMe();
}
class MyShowrableEx3 : AbstractShowrable
{
    public override void ShowMe() => Console.WriteLine("MyShowrableEx3クラスのShowMe");
}

インターフェースメソッドを呼び出すと。

var myShowrableEx3 = new MyShowrableEx3();
((IShowrable)myShowrableEx3).ShowMe();

実行結果

MyShowrableEx3クラスのShowMe

明示的実装が必要な場合は再度インターフェースを指定し直して

class MyShowrableEx4 : MyShowrableBase, IShowrable
{
    void IShowrable.ShowMe() => Console.WriteLine("MyShowrableクラスIShowrableインターフェースのShowMeメソッドの明示的実装");
}

インターフェースの継承

インターフェースの継承は可能、その例を示す。
IShowrableインターフェースを継承したIShowrableExインターフェースを定義し、IShowrableExインターフェースを実装するMyShowrableEx5クラスを定義。

interface IShowrableEx : IShowrable
{
    void ShowMeEx();
}
class MyShowrableEx5 : IShowrableEx
{
    void IShowrable.ShowMe() => Console.WriteLine("IShowrableインターフェースのShowMeメソッドの明示的実装");
    void IShowrableEx.ShowMeEx() => Console.WriteLine("IShowrableExインターフェースのShowMeExメソッドの明示的実装");
}

MyShowrableEx5クラスのインスタンスを生成してインターフェースのメソッドを実行してみる。

var myShowrableEx5 = new MyShowrableEx5();
((IShowrable)myShowrableEx5).ShowMe();
((IShowrableEx)myShowrableEx5).ShowMeEx();

実行結果

IShowrableインターフェースのShowMeメソッドの明示的実装
IShowrableExインターフェースのShowMeExメソッドの明示的実装

プロパティ、インデクサ、イベントもインターフェイスに

他のサイトからの引用だが

上記の記事より

public delegate void SampleEvent(object sender, EventArgs e);
public interface ISample
{
  int SampleProperty { get; set; }
  string this[int index] { get; set; }
  event SampleEvent sampleEvent;
}
ページのトップへ戻る