連載
プロフェッショナルVB.NETプログラミング
第23回 処理を委譲するデリゲート
(株)ピーデー
川俣 晶
2002/11/02
|
|
継承でも実現できるか?
異なるクラスのメソッドを1つのメソッド呼び出しで扱うというのは、何もデリゲートだけが持つ機能ではない。クラスの継承や、インターフェイスの機能を使ってもこれは実現できる。以下は継承を使って実現してみた例である。これを、デリゲートを使った場合と比較して考えてみよう。
1: Public MustInherit Class ClassBase
2: Public MustOverride Function Method(ByVal x As Integer, ByVal y As Integer) As Integer
3: End Class
4:
5: Public Class ClassMult
6: Inherits ClassBase
7: Public Overrides Function Method(ByVal x As Integer, ByVal y As Integer) As Integer
8: Return x * y
9: End Function
10: End Class
11:
12: Public Class ClassPlus
13: Inherits ClassBase
14: Public Overrides Function Method(ByVal x As Integer, ByVal y As Integer) As Integer
15: Return x + y
16: End Function
17: End Class
18:
19: Public Class Form1
20: Inherits System.Windows.Forms.Form
21:
22: …Windows フォーム デザイナで生成されたコード…
23:
24: Private Sub calc(ByVal x As Integer, ByVal y As Integer, ByRef instance As ClassBase)
25: Dim result As Integer = instance.Method(x, y)
26: Trace.WriteLine(result)
27: End Sub
28:
29: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
30: Dim instance1 As New ClassMult()
31: Dim instance2 As New ClassPlus()
32: calc(2, 3, instance1)
33: calc(2, 3, instance2)
34: End Sub
35: End Class
|
|
継承を使用し、1つのメソッド呼び出しで異なるクラスのメソッドを呼び出すサンプル・プログラム7 |
これを実行すると以下のようになる。
このサンプル・プログラムは、25行目のメソッド呼び出しによって、掛け算をしたり、足し算をしたりする。機能面では、これまで説明したサンプル・プログラムと似ているが、コーディングにはいくつもの違いが見られる。まず、継承を使う方法ではメソッド名を統一しておく必要がある。また、あらかじめ基底クラス(ClassBase)でMustOverrideなメソッド宣言をしておき、機能を実装する場合はOverridesキーワードを付けてメソッドを記述する必要がある。戻り値と引数が合っていれば使用できるデリゲートと比較すれば、制約は大きいといえる。さらに、25行目を見て分かるとおり、具体的に呼び出すインスタンスとメソッドが直接記述されており、代表者としての役割を果たしているとはいい難い。
このようにデリゲートはデリゲートという1つの機能であって、継承やインターフェイスとは異なる特徴を持っている。これらは、適切に使い分けられるべきものなのである。
デリゲートをオブジェクトとして使う
デリゲートのインスタンスも、当然ながら一種のインスタンスである。これらは、System.DelegateクラスやSystem.MulticastDelegateクラスを継承しており、これらのクラスのメソッドやプロパティを呼び出すことができる。以下はその機能を用いて、デリゲート・インスタンスが指し示しているメソッドとインスタンスの属するクラス名を出力するようにした例である。
1: Delegate Sub SampleDelegate()
2:
3: Public Class Form1
4: Inherits System.Windows.Forms.Form
5:
6: …Windows フォーム デザイナで生成されたコード…
7:
8: Private Sub method()
9: Trace.WriteLine("Hello!")
10: End Sub
11:
12: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
13: Dim sample As SampleDelegate = AddressOf method
14: Trace.WriteLine(sample.Method.Name)
15: Trace.WriteLine(sample.Target.GetType().FullName)
16: End Sub
17: End Class
|
|
デリゲートのインスタンスが指し示しているメソッドと、インスタンスの属するクラス名を出力するサンプル・プログラム8 |
これを実行すると以下のようになる。
1: method
2: Sample007n.Form1
|
|
サンプル・プログラム8の実行結果 |
14行目のsample.Method.Nameは、デリゲートが持つメソッドに関する情報を、Methodプロパティを通じて取り出し、さらにメソッドの名前を持つNameプロパティを読み出している。15行目のsample.Target.GetType().FullNameは、まず、デリゲート・インスタンスが対象とするインスタンスをTargetプロパティで取り出し、それに対してGetType().FullNameを適用することで、クラス名を取り出している。暗黙のうちに、このサンプル・プログラムのプロジェクト名である「Sample007n」と同じ名前の名前空間が仮定されるので、クラスのフルネームはSample007n.Form1となっている。
privateなメソッドへの委譲
デリゲートを用いてアクセス権限のないメソッドへ委譲することはできないが、アクセス権限のないメソッドを呼び出せないわけではない。デリゲート・インスタンスを作成する時点で、呼び出すべきメソッドへのアクセス権限があればよいのであって、メソッド呼び出しそのものを行うときにはアクセス権限がなくてもよい。実際に、アクセス権限のないメソッド呼び出しが、デリゲートを経由することで可能になっている例を以下に示す。
1: Public Delegate Sub SampleDelegate(ByVal message As String)
2:
3: Public Class SampleClass
4: Private Sub outputMessage(ByVal message As String)
5: Trace.WriteLine(message)
6: End Sub
7:
8: Public Function GetDelegate() As SampleDelegate
9: Return AddressOf MyClass.outputMessage
10: End Function
11: End Class
12:
13: Public Class Form1
14: Inherits System.Windows.Forms.Form
15:
16: …Windows フォーム デザイナで生成されたコード…
17:
18: Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
19: Dim instance As New SampleClass()
20: Dim sample As SampleDelegate = instance.GetDelegate()
21: sample("Hello!")
22: End Sub
23: End Class
|
|
アクセス権限のないメソッドをデリゲート経由で呼び出すサンプル・プログラム9 |
これを実行すると以下のようになる。
もし、21行目に、4〜6行目のoutputMessageメソッドへの呼び出しを記述した場合、コンパイル・エラーになるのは当たり前である。しかし、このサンプル・プログラムは正常に動作する。そのポイントは、デリゲート・インスタンスを作成するとき(9行目)には、outputMessageメソッドへのアクセス権がある、という点にある。もし、9行目をForm1クラス内に移動させたら、このソースはコンパイルできなくなる。
Insider.NET 記事ランキング
本日
月間