上記の例を見て、掛け算も足し算も完全に独立した処理であり、staticなメソッドにしても何ら問題ないことに気づいた人もいるだろう。では、staticなメソッドへの委譲は記述できるのだろうか? List 13-3はそのように修正してみたものである。
1: using System;
2:
3: namespace Sample003
4: {
5: delegate int Sample( int x, int y );
6: class Class2
7: {
8: public static int methodMult( int x, int y )
9: {
10: return x*y;
11: }
12: public static int methodPlus( int x, int y )
13: {
14: return x+y;
15: }
16: }
17: class Class1
18: {
19: public static void calc( int x, int y, Sample calcMethod )
20: {
21: int result = calcMethod( x, y );
22: Console.WriteLine( result );
23: }
24: [STAThread]
25: static void Main(string[] args)
26: {
27: calc( 2, 3, new Sample( Class2.methodMult ) );
28: calc( 2, 3, new Sample( Class2.methodPlus ) );
29: }
30: }
31: }
これを実行するとFig.13-3のようになる。
ほかの多くの場合と同様に、staticなメソッドを指定するために、インスタンス名の代わりにクラス名を使用することができる。27行目のClass2.methodMultのようにメソッド名を記述すれば、そのままstaticなメソッドを利用可能である。それ以外には特別な記述はなく、デリゲートの宣言や呼び出し時に特別な配慮は必要ないことが分かると思う。
これは非常に重要なことなので、よく頭に刻み込んでほしい。デリゲートは、staticなメソッドを使う場合を除き、インスタンスとメソッドの情報を扱うのである。つまり、デリゲートが委譲する先は、ただ単に指定されたメソッドが呼ばれるだけでなく、呼び出し時にインスタンスも明示される。List 13-4は、複数インスタンスのメソッドを委譲先として使用したサンプル・ソースである。
1: using System;
2:
3: namespace Sample004
4: {
5: delegate void Sample( int x, int y );
6: class Class2
7: {
8: public int result;
9: public void method( int x, int y )
10: {
11: result = x * y;
12: }
13: }
14: class Class1
15: {
16: [STAThread]
17: static void Main(string[] args)
18: {
19: Class2 instance1 = new Class2();
20: Class2 instance2 = new Class2();
21: Sample d1 = new Sample( instance1.method );
22: Sample d2 = new Sample( instance2.method );
23: d1(2,3);
24: d2(4,5);
25: Console.WriteLine( instance1.result );
26: Console.WriteLine( instance2.result );
27: }
28: }
29: }
これを実行するとFig.13-4のようになる。
ここでは、同じClass2クラスのインスタンスを2つ作成している(19〜20行目)。この2つのインスタンスに対して別々にデリゲート・インスタンスを作成している(21〜22行目)。さて、ここからがこのサンプル・ソース最大の注目点なのだが、23〜24行目のメソッド呼び出しには、どのClass2インスタンスを対象にするか、明示的には表現されていない。しかし、デリゲート・インスタンスは、どのClass2インスタンスを対象とするかを覚えているので、間違いなく、21〜22行目で指定されたClass2インスタンスのメソッドを呼び出す。
このように、デリゲートはメソッドだけでなくインスタンスも識別することをよく覚えておいていただきたい。特にC/C++プログラマーの方々は注意していただきたい。関数ポインタは関数を特定する機能しか持たず、インスタンスを特定する情報は持たないのである。
デリゲートは、対象となるメソッドにprivateキーワードなどが付加されてアクセス権が与えられていない場合などを除き、戻り値と引数が合えば委譲できる。別クラスの異なる名前のメソッドであっても、戻り値と引数だけ合っていれば委譲できる。List 13-5は、まったく無関係な別クラスの異なる名前のメソッドに委譲した例である。
1: using System;
2:
3: namespace Sample005
4: {
5: delegate void Sample( int x, int y );
6: class Class2
7: {
8: public int result;
9: public void methodMult( int x, int y )
10: {
11: result = x * y;
12: }
13: }
14: class Class3
15: {
16: public int result;
17: public void methodPlus( int x, int y )
18: {
19: result = x + y;
20: }
21: }
22: class Class1
23: {
24: [STAThread]
25: static void Main(string[] args)
26: {
27: Class2 instance1 = new Class2();
28: Class3 instance2 = new Class3();
29: Sample d1 = new Sample( instance1.methodMult );
30: Sample d2 = new Sample( instance2.methodPlus );
31: d1(2,3);
32: d2(4,5);
33: Console.WriteLine( instance1.result );
34: Console.WriteLine( instance2.result );
35: }
36: }
37: }
これを実行するとFig.13-5のようになる。
List 13-5はここまで説明した知識だけで読み取れると思う。このように、デリゲートは自由度の高い機能なのである。
Copyright© Digital Advantage Corp. All Rights Reserved.