連載

C#入門

第15 インターフェイスの活用

(株)ピーデー
川俣 晶
2001/11/10


インターフェイスの効能

 インターフェイスの機能はすでに第4回で紹介済みなので、いまさらという印象を受けるかもしれない。しかし、まだまだインターフェイスについて語っていない内容も多い。

 初心者の場合、自分でインターフェイスを定義する機会は多くないかもしれない。しかし、システム側で定義しているいくつかのインターフェイスを自作クラスに実装すると、いろいろと便利な機能が有効になる。この方法は初心者でも知っておく価値があるだろう。

 今回は、インターフェイスの機能概要を説明した後で、システムが定義するインターフェイスを活用すると何ができるのかを紹介しよう。

 さて、まずは、インターフェイスの最も基本的な効能から説明しよう。

 以下のサンプルソースでは、継承関係がまったくないクラスであるClass2とClass3を定義している。しかし、この2つのクラスには、同じtaskという名前のメソッドがある。ある外部のメソッドから、Class2とClass3の違いに関係なく、taskメソッドを呼び出したいとしよう。これをインターフェイスを用いて解決した例が以下である。

   1: using System;
   2:
   3: namespace ConsoleApplication93
   4: {
   5:   interface ISpecialTask
   6:   {
   7:     void task();
   8:   }
   9:   class Class2 : ISpecialTask
  10:   {
  11:     public void task()
  12:     {
  13:       Console.WriteLine("task() in Class2");
  14:     }
  15:   }
  16:   class Class3 : ISpecialTask
  17:   {
  18:     public void task()
  19:     {
  20:       Console.WriteLine("task() in Class3");
  21:     }
  22:   }
  23:   class Class1
  24:   {
  25:     static void callTask( ISpecialTask ist )
  26:     {
  27:       ist.task();
  28:     }
  29:     static void Main(string[] args)
  30:     {
  31:       Class2 c2 = new Class2();
  32:       Class3 c3 = new Class3();
  33:       callTask( c2 );
  34:       callTask( c3 );
  35:     }
  36:   }
  37: }
インターフェイスの最も基本的な効能を示すサンプル・プログラム1
異なるクラスにある同じ名前のメソッドを、クラスの違いとは無関係に呼び出す。

 これを実行すると以下のようになる。

サンプル・プログラム1の実行結果
インターフェイスへの参照を利用して、メソッドが呼び出される。

 ここでポイントになるのは、25〜28行目のメソッドcallTaskである。このメソッドの中から、Class2とClass3に含まれるtask()を呼び出したいのである。だが、通常は継承関係もないクラス間で、共通のメソッドを持つことはできない。なお、厳密にいえば、すべてのクラスは、System.Objectから派生しているものなので、継承関係がないといえば嘘になるのだが、System.Objectは勝手に書き換えられないクラスなので、この手の問題解決には役に立たないため範囲外と考え、無関係と記述することにする。

 それはさておき、無関係なクラス間で共通のメソッドを持つには、インターフェイスを共有する、という方法がある。上記のソースは、それを実践した例である。見て分かるとおり、Class2とClass3は、共通のインターフェイスISpecialTaskを実装している。5〜8行目で、ISpecialTaskはtask()というメソッドを実装すべきことを示している。ここではメソッドの内容は記述されない。あくまで、引数と戻り値だけが指定される。この例ではどちらもないことが明示されている。そして、9行目と16行目は、それぞれのクラスが、このインターフェイスを実装することを明示的に指定している。実装内容は、11〜14行目と、18〜21行目にある。そして、25〜28行目のcallTaskメソッドは、引数としてISpecialTaskインターフェイスへの参照を取るように記述されている。ISpecialTaskを実装したクラスのインスタンスからは、必ずこのインターフェイスへの参照を取得できる。つまり、Class2のインスタンスからも、Class3のインスタンスからも取得できることになる。そのため、33〜34行目のように、どちらのクラスのインスタンスも、引数に渡すことができる(厳密にいえば、インスタンスへの参照から、インターフェイスへの参照が取り出されて、それがメソッドに渡る)。

継承で実現した例

 上の例は、インターフェイスを使わなくても、継承を使って実現できる。実際に継承を使った例を以下に示す。

   1: using System;
   2:
   3: namespace ConsoleApplication94
   4: {
   5:   abstract class ClassSpecialTask
   6:   {
   7:     public abstract void task();
   8:   }
   9:   class Class2 : ClassSpecialTask
  10:   {
  11:     public override void task()
  12:     {
  13:       Console.WriteLine("task() in Class2");
  14:     }
  15:   }
  16:   class Class3 : ClassSpecialTask
  17:   {
  18:     public override void task()
  19:     {
  20:       Console.WriteLine("task() in Class3");
  21:     }
  22:   }
  23:   class Class1
  24:   {
  25:     static void callTask( ClassSpecialTask ist )
  26:     {
  27:       ist.task();
  28:     }
  29:     static void Main(string[] args)
  30:     {
  31:       Class2 c2 = new Class2();
  32:       Class3 c3 = new Class3();
  33:       callTask( c2 );
  34:       callTask( c3 );
  35:     }
  36:   }
  37: }
インターフェイスではなく継承を使用したサンプル・プログラム2
プログラムの構成はインターフェイスを使用した場合とほぼ同じになっている。

 これを実行すると以下のようになる。

サンプル・プログラム2の実行結果
実行結果も当然インターフェイスを使用した場合と同じになる。

 すでに継承は説明済みなので、ソースコードの解説は割愛する。ここで理解していただきたいことは、インターフェイスを1つだけ実装する場合は、継承を用いても、ほぼ同等の機能を実現できるということである。しかし、実装するインターフェイスが2つになると、継承で類似機能を記述することはできなくなる。以下は、クラスが2個のインターフェイスを実装している例である。

   1: using System;
   2:
   3: namespace ConsoleApplication95
   4: {
   5:   interface ISpecialTask1
   6:   {
   7:     void task1();
   8:   }
   9:   interface ISpecialTask2
  10:   {
  11:     void task2();
  12:   }
  13:   class Class2 : ISpecialTask1, ISpecialTask2
  14:   {
  15:     public void task1()
  16:     {
  17:       Console.WriteLine("task1() in Class2");
  18:     }
  19:     public void task2()
  20:     {
  21:       Console.WriteLine("task2() in Class2");
  22:     }
  23:   }
  24:   class Class3 : ISpecialTask1, ISpecialTask2
  25:   {
  26:     public void task1()
  27:     {
  28:       Console.WriteLine("task1() in Class3");
  29:     }
  30:     public void task2()
  31:     {
  32:       Console.WriteLine("task2() in Class3");
  33:     }
  34:   }
  35:   class Class1
  36:   {
  37:     static void callTask1( ISpecialTask1 ist )
  38:     {
  39:       ist.task1();
  40:     }
  41:     static void callTask2( ISpecialTask2 ist )
  42:     {
  43:       ist.task2();
  44:     }
  45:     static void Main(string[] args)
  46:     {
  47:       Class2 c2 = new Class2();
  48:       Class3 c3 = new Class3();
  49:       callTask1( c2 );
  50:       callTask1( c3 );
  51:       callTask2( c2 );
  52:       callTask2( c3 );
  53:     }
  54:   }
  55: }
2つのインターフェイスを実装したサンプル・プログラム3
継承では類似の機能を記述することはできない。

 これを実行すると以下のようになる。

サンプル・プログラム3の実行結果
2つのクラスにある2つのメソッドが、それぞれ順に呼び出される。

 インターフェイスも継承も、ソースコード上では同じように見える。クラス宣言のクラス名の後に、コロン記号(:)を書いて、その後にインターフェイスか継承するクラス名を記述する。だが、両者の間で決定的に違うのは、インターフェイスの名前は何個でも記述できるのに対して、継承するクラス名は1個しか記述できないことである。

 このような制限は、技術的なものではない。事実C++では、継承するクラス名をいくつ書いてもよい。これを多重継承という。多重継承ができれば、インターフェイスはなくてもよい。事実、C++にインターフェイスはない。にもかかわらず、C#やJavaといった比較的新しい言語で多重継承が禁止されているのは、継承が過剰に強力すぎる機能であり、トラブルを引き起こしやすいという経験による。強力すぎる継承の機能を、適正な水準まで制限した結果が、継承するクラスは1個までという制約と、インターフェイスの導入である。そのようなわけで、C#では、継承とインターフェイスを適切に使い分けねばプログラムが組めない。どういうときに、どちらを使うのかは、.NET Frameworkのリファレンスなど、実例を見ているうちに、何となく分かってくると思う。おおざっぱにいえば、機能性を継承するときは継承を使い、呼び出し方法を合わせるときはインターフェイスを使うのである。

 

 INDEX
  第15回 インターフェイスの活用
  1.インターフェイスの効能
    2.インターフェイスの継承
    3.同名のメソッドを持つインターフェイス
    4.ソート順を操る

「C#入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間