連載

改訂版 プロフェッショナルVB.NETプログラミング

Chapter 11 デリゲート

株式会社ピーデー 川俣 晶
2004/07/22
Page1 Page2 Page3 Page4

 
 本記事は、(株)技術評論社が発行する書籍『VB6プログラマーのための入門 Visual Basic .NET 独習講座』の一部分を許可を得て転載したものです。同書籍に関する詳しい情報については、本記事の最後に掲載しています。

 何かの処理を行う際にメソッドなどを直接呼び出すのではなく、間接的なデリゲートと呼ばれる機構を経由して呼び出すことができる。.NET Frameworkのクラス・ライブラリの一部が、デリゲートの機能に依存しているので、クラス・ライブラリをフル活用しようと思うと避けて通れない。クラス・ライブラリを活用するためにも、デリゲートの機能を理解しておこう。

デリゲートとは何か

 デリゲートは、VB.NETの新機能というよりは、.NET Frameworkの実行環境が提供する新しいデータ型である。.NET Frameworkのクラス・ライブラリの一部が、デリゲートの機能に依存しているので、クラス・ライブラリをフル活用しようと思うと、避けて通れない。そのため、VB.NETでもデリゲートが用意されたようだ。しかし、実行環境が提供するデータ型のサポートであるため、同じ.NET Framework上で利用可能なプログラム言語間で、利用方法が似通っている。

 そこで本章は、拙著のC#入門書である『新プログラミング環境 C#がわかる+使える』の13章「処理を委譲するデリゲート」となるべく同じ構成になるようにしている。これらを読み比べていただくと、VB.NETとC#ではデリゲートの使い方が微妙に異なっていることが分かるだろう。それにより、VB.NETはVBの文法で書くC#ではなく、VB.NETという独立した言語であることが実感できると思う。

『@IT技術叢書シリーズ 新プログラミング環境 C#がわかる+使える』

川俣晶 著
ISBN4-7741-1484-7/定価 2380円/技術評論社発行

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

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

 最初に注意点を述べる。デリゲートの機能は、この次のChapter 12で説明するイベントの機能によく似ているように思われるかもしれない。しかし、デリゲートはデータ型の一種であり、イベントはクラスや構造体などのメンバである。両者は目的も使用方法も異なっており、それぞれ別個のものとして学ぶ必要がある。どちらか一方だけを覚えて、他方は使わないという方法はうまく行かないだろう。

 リスト11-1は、デリゲート経由でメソッドを呼び出すサンプル・プログラムである。

 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
リスト11-1 デリゲート経由でメソッドを呼び出すプログラム

 これを実行すると次のようになる(リスト11-2)。

1: 6
リスト11-2 リスト11-1の実行結果

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

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

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

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

Dim sample As New SampleDelegate(AddressOf SampleInstance.SampleMethod)

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

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

 デリゲートとは何かで示したリスト11-1を見ても、デリゲートの利用価値はいまひとつ分からないかもしれない。そこで、デリゲートを使って異なる処理を一元化する例をリスト11-3に示す。

 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
リスト11-3 デリゲートを使って異なる処理を一元化したプログラム

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

1: 6
2: 5
リスト11-4 リスト11-3の実行結果

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

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

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

 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
リスト11-5 リスト11-1の委譲先となる2つのメソッドをクラス内に移動し、省略記法を用いたプログラム

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

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


 INDEX
  [連載] 改訂版 プロフェッショナルVB.NETプログラミング
  Chapter 11 デリゲート
  1.デリゲートとは何か/2つの異なるメソッドに委譲する
    2.共有メソッドへの委譲/デリゲートはインスタンスを選ぶ/戻り値と引数が合えば委譲できる
    3.継承でも実現できるか?/デリゲートをオブジェクトとして使う/Privateキーワードの付いたメソッドへの委譲
    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メールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)
- PR -

注目のテーマ

業務アプリInsider 記事ランキング

本日 月間
ソリューションFLASH