ソフトウェア

C# デリゲートを理解する

はじめに

今回はC# デリゲートについて理解しましょう。
デリゲートはC言語の関数ポインタに近いものです。
ということは関数ポインタがC言語の中で難しい壁だったようにデリゲートでC#に苦手意識を持つ人も多いです。
デリゲートについてどこよりもわかりやすく、解説していきます笑

デリゲートとは

デリゲートは一見、関数の形をした変数型といえます。
そして、このデリゲートはC#ではオリジナルのイベントを作成するのに必要不可欠なものです。

「C#ではデリゲート=オリジナルイベント作成のために必要なもの

デリゲートを作る前に最初に行うデリゲート宣言についてまとめました。

デリゲート宣言のPOINT
デリゲート宣言(delegate) = 変数型の追加と同じ
※デリゲートは関数宣言のようにみえるが、
関数バージョンの変数型の追加と同じ

      /// 【デリゲートの場合】
      delegate void XXXX( ... );
      XXXX aaaa  = func ( ... );
      
      /// 【一般の変数やクラスの場合】
      int aaaa = 100;
      char aaaa = 'A';
      string aaaa  = "ABCDE";
      public class TestDayoClass = new TestDayoClass();
    

デリゲートとintやcharなどの変数型やクラスと大きく違うのはオリジナルのものを宣言するとき、delegateを使って変数の宣言みたいなことを行う必要がある点ですね。
もちろん既存のデリゲートを使う場合、例えばActionなどでは改めての宣言は必要ありません。

そして、C#でオリジナルのデリゲートを作る場合はたいていオリジナルのイベントを作りたい場合が多いです。
その点も含めて、デリゲートの宣言からオリジナルのイベントを利用するまでの過程をサンプルリストを用いてみていきましょう。

サンプルコードを用いて解説

デリゲート(関数型)の宣言

/// <summary>
/// xxxデリゲートの宣言
/// </summary>
/// <param name="sender">発行元オブジェクト</param>
/// <param name="e">付加情報</param>
public delegate void xxxEventHandler(object sender, xxxEventArgs e);

宣言は上のような感じです。
注意したいのはxxxEventHandlerは関数ではない!関数の形をした変数ということをおさえておきましょう。

イベントの宣言

/// <summary>
/// xxxイベント
/// </summary>
public event xxxEventHandler xxxEvent;

C#でオリジナルのデリゲートを作る場合はたいていオリジナルのイベントを作りたい場合が多いです。
作ったデリゲートとイベントを紐づけたい場合は上のような感じで行います。

event 【オリジナルのデリゲート(関数の形をした変数)】 【イベントオブジェクトの名前】

これでオリジナルのイベントはできました。

イベントハンドラの本体定義

次にオリジナルのイベントが発生したときに行わせたい処理を行う関数を作ります。
イベントハンドラとかコールバック関数とか聞いたことがあると思います。
あれと同じです。

// 古い、削除するイベント関数
static void OldRemoveEvent(object sender, xxxEventArgs e)
{
    ...
    Console.WriteLine("OldRemoveEvent関数が呼ばれたよ");
    ...
}

// 新しい、追加するイベント関数
static void NewAddEvent(object sender, xxxEventArgs e)
{
    ...
    Console.WriteLine("NewAddEvent関数が呼ばれたよ");
    ...
}

今回は上の2つの関数を作ってみました。
関数を作るときに必要なポイントは
戻り値と引数を一番最初に作ったデリゲートと同じ形にするという点です。

デリゲートは
void xxxEventHandler(object sender, xxxEventArgs e)
でした。

そして追加した関数は
OldRemoveEvent(object sender, xxxEventArgs e)
NewAddEvent(object sender, xxxEventArgs e)

一致していますね。
ただ、まだオリジナルのイベントxxxEventと上の関数は紐づいていません。

イベントへのイベント関数の登録追加・削除(イベントとハンドラの紐付け)

オリジナルのイベントと用意したイベント関数を紐けます。

// イベント実行
private static void runEvent(xxxEventArgs e)
{
    ...
    /// イベントへハンドラ登録削除
    xxxEvent -= OldRemoveEvent;

    /// イベントへハンドラ追加登録
    xxxEvent += NewAddEvent;
    ...
}

"+"や"-"演算子でイベントオブジェクトに対して関数を追加登録したり、削除できます

これでオリジナルのイベントと用意したイベント関数が紐づきました。
ただ、OldRemoveEvent関数は用意したものの、ここでイベントからわざわざ登録削除しているので、最終的に使われませんが。

イベントの発生

オリジナルのイベントを発生させて、オリジナルのイベント関数を実行しましょう。

/// <summary>
/// xxxイベントをテスト発生する場所
/// </summary>
private void TestFunc()
{
    ...
    /// eventクラスのInvoke関数でイベント発生・点火
    xxxEvent?.Invoke(this, args);
    ...
}

eventクラスのInvoke関数でイベント発生・点火できます。
すると、イベントに登録された関数(NewAddEvent等)が登録された順番に呼ばれていきます。
呼ばれた関数は非同期に実行されます。

ちなみに次のような形でもイベントを発生させることができます。

/// <summary>
/// xxxイベントをテスト発生する場所
/// </summary>
private void TestFunc()
{
    ...
    /// Actionデリゲートと同じ方法で発生・点火
    xxxEvent(this, args);
    ...
}

最後に

C#のデリゲートについて紹介してきました。

デリゲートは関数の形をした変数型
C#においてはデリゲート=オリジナルイベント作成のために必要なもの

デリゲートについて理解が深まる転機になれば幸いです。

-ソフトウェア