連載

プロフェッショナルVB.NETプログラミング
―― VB 6プログラマーのためのVB.NET入門 ――

第23回 処理を委譲するデリゲート

(株)ピーデー
川俣 晶
2002/11/02

Page1 Page2 Page3 Page4

デリゲートとは何か

 デリゲートは、VB.NET(Visual Basic .NET)の新機能というよりは、.NET Frameworkの実行環境が提供する新しいデータ型である。.NET Frameworkのクラス・ライブラリの一部が、デリゲートの機能に依存しているので、クラス・ライブラリをフル活用しようと思うと、避けて通れない。そのため、VB.NETでもデリゲートが用意されたようだ。しかし、実行環境が提供するデータ型のサポートであるため、同じ.NET Framework上で利用可能なプログラム言語間で、利用方法が似通っている。そこで、ここでは「連載:C#入門」の「第17回 処理を委譲するdelegate」のVB.NET版を作成してみた。読み比べると、VB.NETとC#ではデリゲートの使い方が微妙に異なっていることが分かるだろう。それにより、VB.NETはVBの文法で書くC#ではなく、VB.NETという独立した言語であることが実感できると思う。

 さて、何かの処理を行う際にメソッドなどを直接呼び出すのではなく、間接的なデリゲートと呼ばれる機構を経由して呼び出すことができる。デリゲートは日本語では「委譲」と訳されることが多い。委譲という言葉が分かりにくいと思うなら、代表者と考えてもよい。何かの処理を実行させたいときに、直接処理機能を持つメソッドを呼び出すのではなく、代表者に処理を求めるのである。代表者自体は処理機能を持っていないが、それを処理できる適切なメソッドを知っており、そのメソッドに処理要求を渡す。

 最も単純な状況を考えてみよう。クラスAのメソッドaは常にクラスBのメソッドbに処理をゆだねるとしよう。このような場合、メソッドaの中にメソッドbを呼び出すコードを書けばよいので、特別な機能は何も必要がない。しかし、常にゆだねる相手が決まっているとは限らないし、ゆだねる相手が常に1つとも限らない。このような状況に対処するために、VB.NETのデリゲート機能が存在するわけである。

 デリゲートは、VB 6(Visual Basic 6.0)には同等の機能が存在しなかった目新しい機能である。しかし、一部のクラス・ライブラリを活用する際には、必須の機能なので、知っておく価値がある。

 最初に注意点を述べる。デリゲートの機能は、この連載の後の回で説明するイベントの機能によく似ているように思われるかもしれない。しかし、デリゲートとイベントでは目的も使用方法も異なっており、それぞれ別個のものとして学ぶ必要がある。どちらか一方だけ覚えて、他方は使わないという方法は上手く行かないだろう。

 さて、以下はデリゲート経由でメソッドを呼び出すサンプル・プログラムである。

  1: Delegate Function SampleDelegate(ByVal x As Integer, ByVal y As Integer) As Integer
  2:
  3: Public Class SampleClass
  4:   Public Function SampleMethod(ByVal x As Integer, ByVal y As Integer) As Integer
  5:     Return x * y
  6:   End Function
  7: End Class
  8:
  9: Public Class Form1
 10:   Inherits System.Windows.Forms.Form
 11:
 12: …Windows フォーム デザイナで生成されたコード…
 13:
 14:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 15:     Dim SampleInstance As New SampleClass()
 16:     Dim sample As SampleDelegate = AddressOf SampleInstance.SampleMethod
 17:     Dim result As Integer = sample(2, 3)
 18:     Trace.WriteLine(result.ToString())
 19:   End Sub
 20: End Class
デリゲート経由でメソッドを呼び出すサンプル・プログラム1

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

 1: 6
サンプル・プログラム1の実行結果

 このソース・コードのポイントは、17行目に記述したsampleというデリゲート呼び出しによって、実際には4〜6行目のSampleMethodというメソッドが呼び出されているということである。つまり、sampleが代表者で、実際の処理をSampleMethodメソッドに委譲している。

 では具体的なコーディングについて説明しよう。まず、1行目でDelegateキーワードが目に付くと思う。Delegateキーワードから書き始めた宣言は、デリゲートのデータ型を規定する。このあと述べるように、デリゲートはさまざまなメソッドを呼び出すために使用できるが、あくまで同じ戻り値、同じ引数のメソッドに限られる。そこで、どのような戻り値、どのような引数のメソッドを対象とするかを、ここで明確にしておくわけである。ここでは、戻り値として行の最後にAs Integerが指定されている。引数はカッコ内に記述されたとおり、整数値が2つ必要となる。最後に、SampleDelegateというキーワードが残ったが、これがデータ型の名前となる。これ以後、SampleDelegateというデータ型を記述することで、この戻り値と引数の条件を満たすデリゲートを示すことができる。逆にいえば、名前さえ変えれば、戻り値や引数の異なるデータ型をいくらでも定義して利用することも可能である。なお、このデリゲートの宣言には、PublicやPrivateなどのキーワードも付けることができる。

 次に見ていただきたいのは、16行目である。ここでは、デリゲートのインスタンスを作成している。AddressOf演算子は、デリゲートのインスタンスを作成する機能を持っている。それに続いて記述されたSampleInstance.SampleMethodにより、SampleInstanceというインスタンスのSampleMethodというメソッドに処理を委譲したい、ということを意味している。ここを変えれば、ほかのインスタンスのほかのメソッドに委譲先を変更できる。もちろん、16行目は、SampleDelegate型の引数および戻り値と、AddressOf演算子で指定したメソッドの引数および戻り値が一致していない場合、コンパイル時にエラーになる。

 なお、16行目は下記のように記述してもコンパイルは成功する。

Dim sample As New SampleDelegate(AddressOf SampleInstance.SampleMethod)

 最後に見ていただきたいのが、17行目である。パッと見ると、普通のメソッド呼び出しと何も変わらないように見える。しかし、実際には処理は委譲されているわけである。一見、何の変哲もないメソッドのように見えるところがデリゲートのポイントである。

2つの異なるメソッドに委譲する

 上記のサンプルを見ても、デリゲートの利用価値はいま1つ分からないかもしれない。そこで、デリゲートを使って異なる処理を一元化する例を以下に示そう。

  1: Delegate Function SampleDelegate(ByVal x As Integer, ByVal y As Integer) As Integer
  2:
  3: Public Class SampleClass
  4:   Public Function MethodMult(ByVal x As Integer, ByVal y As Integer) As Integer
  5:     Return x * y
  6:   End Function
  7:
  8:   Public Function MethodPlus(ByVal x As Integer, ByVal y As Integer) As Integer
  9:     Return x + y
 10:   End Function
 11: End Class
 12:
 13: Public Class Form1
 14:   Inherits System.Windows.Forms.Form
 15:
 16: …Windows フォーム デザイナで生成されたコード…
 17:
 18:   Private Sub calc(ByVal x As Integer, ByVal y As Integer, ByVal calcMethod As SampleDelegate)
 19:     Dim result As Integer = calcMethod(x, y)
 20:     Trace.WriteLine(result)
 21:   End Sub
 22:
 23:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 24:     Dim instance As New SampleClass()
 25:     calc(2, 3, AddressOf instance.MethodMult)
 26:     calc(2, 3, AddressOf instance.MethodPlus)
 27:   End Sub
 28: End Class
デリゲートを使って異なる処理を一元化したサンプル・プログラム2

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

 1: 6
 2: 5
サンプル・プログラム2の実行結果

 このサンプル・プログラムで意図しているのは、18〜21行目のcalcメソッドで、さまざまな計算を実行させたい、ということである。ここでは掛け算と足し算をメソッドとして用意している。計算方法の変更は、呼び出すメソッドを変えるだけで実現できるのは明らかだ。4〜6行目のMethodMultメソッドを呼び出せば掛け算になり、8〜10行目のMethodPlusメソッドを呼び出せば足し算になる。そこで、calcメソッドは第3引数として、デリゲートのインスタンスを受け取るようになっている。このデリゲートのインスタンスの委譲先として指定するメソッドを入れ替えれば、計算内容も変わるというわけだ。

 実際に25行目では、MethodMultメソッドに委譲するデリゲート・インスタンスを作成して渡している。また26行目では、同様にMethodPlusメソッドを使っている。これにより、19行目で行われる計算内容は(より正確には19行目から呼び出されるメソッドは)、25行目から呼ばれたときと26行目から呼ばれたときでは異なることになる。

 なお、同じインスタンスのメソッドを呼び出す場合には省略記法が使用できる。以下はそれを用いた例である。

  1: Delegate Function SampleDelegate(ByVal x As Integer, ByVal y As Integer) As Integer
  2:
  3: Public Class Form1
  4:   Inherits System.Windows.Forms.Form
  5:
  6: …Windows フォーム デザイナで生成されたコード…
  7:
  8:   Public Function MethodMult(ByVal x As Integer, ByVal y As Integer) As Integer
  9:     Return x * y
 10:   End Function
 11:
 12:   Public Function MethodPlus(ByVal x As Integer, ByVal y As Integer) As Integer
 13:     Return x + y
 14:   End Function
 15:
 16:   Private Sub calc(ByVal x As Integer, ByVal y As Integer, ByVal calcMethod As SampleDelegate)
 17:     Dim result As Integer = calcMethod(x, y)
 18:     Trace.WriteLine(result)
 19:   End Sub
 20:
 21:   Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
 22:     calc(2, 3, AddressOf MyClass.MethodMult)
 23:     calc(2, 3, AddressOf MethodPlus)
 24:   End Sub
 25: End Class
サンプル・プログラム1の委譲先となる2つのメソッドをクラス内に移動し、省略記法を用いたサンプル・プログラム3

 22行目のMyClassキーワードは同じクラスのメソッドを呼び出すことを示すキーワードである。あるいは23行目のように、メソッド名だけを記述してもよい。

 このほかに、MyClassキーワードが同じクラスのメソッドを指定するのと同じように、基底クラスのメソッドを指定するためのMyBaseキーワードが使用できる。


 INDEX
  連載 プロフェッショナルVB.NETプログラミング
  第23回 処理を委譲するデリゲート
  1.デリゲートとは何か
    2.共有メソッドへの委譲
    3.継承でも実現できるか?
    4.委譲先の合成
 
「プロフェッショナルVB.NETプログラミング」


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 記事ランキング

本日 月間