(:3[kanのメモ帳]

個人ゲーム開発者kan.kikuchiのメモ的技術ブログ。月木更新でUnity関連がメイン。

デリゲートとは【C#】


このエントリーをはてなブックマークに追加

はじめに

DelegateとEventって何が違うの?

だとか、

ラムダ式ってどうやって記述するの?

という状態なので、

今回は勉強がてら、デリゲートについてまとめてみた記事です。


デリゲートとは

そもそもデリゲートとは何かと言えば、

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


との事。もっと簡潔に言うのであれば、

ざっくり言うと、デリゲートとは関数を入れられる変数です。

[C#]デリゲートとはなんぞや – gomokulog


みたいな感じ。

自分で説明しようとしたけど、ググった先の方が分かり易かったパターンですねはい。


デリゲートの使い道としてよくあるのがコールバックで、

その処理終わったらこのメソッド実行して!

みたいな形でメソッドを渡す場合に使います。


使い方

まずデリゲートを使った例を以下に示します。


処理内容はTestというクラスのMethodを実行すると、Test2のインスタンスを作成し、

Test2のMethodを実行するというものです。


このTest2のMethodを実行する際に、

Testに実装されているCallBackというメソッドをデリゲートとして渡しています。

public class Test1 {

	//Test2を作成しMethodを実行する
	public void Method(){
		Test2 test2 = new Test2();
		
                Test2.Delegate delegateMethod = CallBack;
		test2.Method(delegateMethod);
	}

	//Test2の処理が終わったら呼ばれる
	public void CallBack(){
		Debug.Log("処理終わったよー");
	}
}

public class Test2 {

	//引数及び返り値のないデリゲート
	public delegate void Delegate();

	//何らかの処理をした後、入力されたデリゲートを実行する
	public void Method(Delegate delegateMethod){
		/*処理*/

		delegateMethod();
	}

}


まず、Test2の

public delegate void Delegate();

の所でどのようなメソッドをデリゲートととして渡せるかを宣言しています。

引数や返り値を付ける事も可能です。


また、Test1の

Test2.Delegate delegateMethod = CallBack;

でデリゲートの宣言を行っています。変数を宣言するのと同様ですね。


もちろん変数を作成せず、

test2.Method(CallBack);

という形でも問題ありません。


このようにメソッドを他の変数と同じ形で使う事ができるのがデリゲートです。


FuncとAction

先ほどの例のように、デリゲートの形式をいちいち宣言するのがめんどくさい!

てな時に使えるのが、FuncとActionです。

Public class Test2 {

	//何らかの処理をした後、入力されたデリゲートを実行する
	public void Method(Action delegateMethod){
		/*処理*/

		delegateMethod();
	}

}


上記のような感じでデリゲートの宣言が必要なくなります。


また、引数や返り値が必要な場合が以下のようになります。

返り値が無い場合がAction、有る場合が Funcです。

//引数がstring
Action<string> action1;
//引数がstringとint
Action<string, int> action2;

//返り値がint
Func<int> func1;
//引数がstring、返り値がint
Func<string, int> func2;


ActionとFuncを使うにはusing System; が必要なので忘れずに。


privateメソッドを渡す

デリゲートではprivateメソッドを渡す事も可能です。

デリゲート・インスタンスを作成する時点で、呼び出すべきメソッドへのアクセス権限があればよいのであって、メソッド呼び出しそのものを行うときにはアクセス権限がなくてもよい。


という事で、先ほどのCallBackというメソッドがprivateメソッドでも問題無く実行出来ます。


複数のメソッドを渡す

デリゲートには複数のメソッドを設定する事が可能です。

以下の例のように+=演算子を用いて追加していきます。

public void Method(){
  Test2 test2 = new Test2();

  Action delegateMethod = CallBack1;
  delegateMethod += CallBack2;
  delegateMethod += CallBack3;

  test2.Method(delegateMethod);
}

//Test2の処理が終わったら呼ばれる
private void CallBack1(){
  Debug.Log("処理終わったよー1");
}
private void CallBack2(){
  Debug.Log("処理終わったよー2");
}
private void CallBack3(){
  Debug.Log("処理終わったよー3");
}


こうするとdelegateMethodを実行するだけで、

CallBack1〜3の3つのメソッドを実行する事が可能です。


匿名メソッド

デリゲートの為だけにメソッドを作るのはちょっと……

みたいな時に使えるのが匿名メソッドです。


例のCallBackメソッドを匿名メソッドにすると以下の通り。

Action delegateMethod = delegate {
  Debug.Log("処理終わったよー1");
};


簡単!引数がなければdelegateの後に{}付け、その中に処理を書くだけ。

引数がある場合は以下のように()内に設定します。

Action<string, int> delegateMethod = delegate(string text, int num) {
  Debug.Log("処理終わったよー1");
};


返り値がある場合は通常のメソッドと同様、returnを使いましょう。


ラムダ式

やっと出ましたラムダ式。

ラムダ式は先ほどの匿名メソッドの一種で、より簡潔に匿名関数が作れます。



先ほどCallBackの匿名メソッドをラムダ式で書くと以下の通りです。

Action delegateMethod = () => Debug.Log("処理終わったよー1");


delegateと{}が無くなり、より簡潔になりました。({}は付けても良い)


間の()は引数を設定する場所ですが、引数が無いのでただの括弧だけになっています。

という事で引数がある場合は以下の通りです。

Action<string, int> delegateMethod = (text, num) => Debug.Log("処理終わったよー1");


なんと、引数の型まで省略できてしまいます。

ちょっと省略し過ぎな気もしますが、かなり簡潔にはなりますね。


Event

さて最後にもう一つの疑問、DelegateとEventの違いについてです。


実はEventはDelegateの一種でほとんど同じ機能を持っていますが、

一部仕様が異なり、それにより使い方も変わってきます。

C# の event は delegate のようにメソッドを登録することができます。
ただし、以下の点が違います。

  • ローカルな変数には使えず、必ずクラスのメンバー
  • 同じクラスのメソッドからのみ呼び出し可能


つまり、自分でメソッドを登録して他のクラスに渡すのでは無く、

他のクラスにメソッドを登録しといてもらい、特定のタイミングでそれを実行するという感じですね。


例えば、ボタンが押されたかを監視しているクラスに、

ボタンが押された際のメソッドを登録するといった具合です。

NGUIのUIButtonが正しくその通りの形になっています。


実装は以下のようにeventを付けるだけです。

public class Test1 {

	//Test2を作成しMethodを実行する
	public void Method(){
		Test2 test2 = new Test2();

		//以下2行エラー、外部から実行と代入は出来ない
		//test2.CallBack();
		//test2.CallBack = () => Debug.Log("処理終わったよー1");

		test2.CallBack += () => Debug.Log("処理終わったよー1");


		test2.Method();
	}

}

public class Test2 {

	public event Action CallBack = null;

	//何らかの処理をした後、登録されたコールバックを実行する
	public void Method(){
		/*処理*/

		if(CallBack != null){
			CallBack();
		}
	}

}


なんとなく自分のイメージでは、

これ使って!と渡すのがDelegate

こっちで使うから登録しといて!とするのがEvent

なのかなと思いました。


もちろんDelegateでEventと同様の機能も実装出来ますが、

安全性を考え、使い分けていきたいものです。