たくあんポリポリ

勉強したことを載せていきます。最近、技術系の記事はZennに書いています。(https://zenn.dev/chittai)

【C#】delegateって何に使うの?

delegateについて説明しましたが、結局何に使用されるのかわからないとあまりピント来ない人は多いハズ。まずはどの様に使われているのか確認したいと思います

delegate の具体的な使い道 -コールバック-

いろんなサイトを調べると、delegateの用途の一つに”コールバックに使用します”といった記述が見つかります。なので、まずはコールバックについて説明します。

コールバック is 何

Aという処理を呼び出した際にAという処理の中でBという関数を実行するようにします。このBをコールバック関数といいます。これだけだと、”いや、ただ別の処理を呼んでるだけじゃん”となるので、さらに条件として、Aを呼び出すときに事前にBの処理の参照情報を渡しておきます。そうすると、呼び出した処理Aの中で、引数として渡したBを実行すれば、呼び出し元(Aを呼び出した側)に実行制御が移ります。これがコールバックです。

事例 of コールバック

呼び出す側

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("DelegateMethodTest Start");
        Test test = new Test();
        test.TestMethod(x => x * x);
    }
}

呼び出されるがわ

class Test
{
    public void TestMethod(Func<int, int> delegateMethod)
    {
        Console.WriteLine("This is DelegateMethod");
        Console.WriteLine(delegateMethod(10));
    }
}

ここでは、ProgramクラスのMainメソッドからTestクラスのTestMethodメソッドを呼び出しています。呼び出すときに”x => x * x”という処理を引数として渡します。x という引数を受け取り、その2乗を返す処理です。

では呼び出される側はどうなっているでしょう。ポイントは、呼び出される側で定義されているTestMethodの引数”Func delegateMethod”です。前の記事に書いたFuncの説明によれば、int型の引数を1つとり、int型の値を返す処理をdelegateMethodとして受け取ることができます。つまり今回は、delegateMethodは、x => x*x を参照している状態になります。

”Console.WriteLine(delegateMethod(10));”は "10を引数に取り、10 * 10 を返す"結果をコンソールに表示します。

抽象化 of コールバック

抽象化というほどでもないのですが、コールバックだから~というよりもメソッドを引数として渡したいと思ったときにデリゲートを思い出せばいいと思います。結果としてコールバックに使ってますが、実際の使用方法の本質は上記なのかなと思います。

delegate の具体的な使い道 -イベントハンドラ-

次のdelegateの用途に移ります。次の用途は”イベントハンドラ”になります。

事例 of イベントハンドラ

イベントハンドラは下記サイトにてこのように説明されています。

「キーボードのボタンが押された」とか「マウスが移動した」等の、 コンピュータ上で発生するなんらかの事象のことをイベント(event)といい、 イベントが発生したときに行う処理のことをイベント ハンドラー(event handler)と呼びます。

イベント - C# によるプログラミング入門 | ++C++; // 未確認飛行 C

では、具体的にコードを見ていきましょう。ただ、キー入力とかイベントになるような処理を入れなかったので、イベントではない気がしますがご容赦ください。

とりま、これがイベントを発行する側です。

class Test
{
    public delegate void DelegateEventHandler();
    public event delegateEventHandler TestEventMethod;
    public void Method()
    {
        TestEventMethod();
    }
}

デリゲートを定義しています。event 構文を使うと、外部からはこのTestEventMethodに処理を登録・削除することしかできなくなります。

    public delegate void DelegateEventHandler();
    public event delegateEventHandler TestEventMethod;

これがイベントを受ける側です。イベント発行時に登録した処理が実行されます。

class Program
{
    static void Main(string[] args)
    {
        Test test = new Test();
        test.TestEventMethod+= () => Console.WriteLine("This is TestEventMethod");
       
        test.Method();
    }
}

ここで、先程定義したTestEventMethodに処理を登録しています。

        test.TestEventMethod+= () => Console.WriteLine("This is TestEventMethod");

このようにして、イベント発行側でデリゲート型の変数TestEventMethodを定義してevent化しておき、別のクラスにてTestEventMethodに処理を登録しておけば、イベント発行側で何かしらのイベントを発行したタイミングで、登録した処理を実行できるようになります。

つまり、イベント発生時にイベントハンドラ(登録した処理)を実行できるようになるということです。

抽象化 of イベントハンドラ

時間が経過したタイミングだったり、イベントが発行した時にGUIの表示を変えたいなど、Aが起きたら→Bをするという流れの中で使うことができ、これで機能的な単位で独立させることができる(時間の経過見ることと、画面に表示するのは別の機能)。つまり、イベントの発行と受け取り(処理の登録)は別のクラスで実施すると良いかと思います。