自分用の学習メモです。
既に多くの人が書いている内容です。そのため、初心者である僕の記事を読むよりも他の人の記事読んだ方が100%良いです (参考サイトを読んだ方がいい)
動機
現在、取り組んでいるドイツのHarz大学との合同ゲームプロジェクトにてゲームを作る上でせっかくなので設計を頑張ってみようと思いました。
ちゃんとした設計をするのは初めてだったので色々と調査をしました。
調査を進める中でUniRxというライブラリを使うと良いということを知りました。これを使ってみたいと思いました。が、その解説の中でDelegate、Eventという知らない概念があったので、それについて調べました。(ついでラムダ式、Action、匿名関数も勉強した)
Delegateとは(概念部分)
C#入門者を苦しめるDelegate、Action、Event、ラムダ式、匿名関数は全てDelegateの派生形です。なのでまずDelegateをわかるようにするのが大切。
Delegateは関数型志向の考え方をプログラムとして実現するための機能です。
関数型志向とは「関数をまとめて変数として扱おうゼ!!」という発想のことです。
delegateの書き方
delegateの使い方
よくあるのはコールバックの中でコールバックに書いた処理が終わった後に追加で何かしらのメソッドを実行したいときにコールバック関数にメソッドごと渡すというパターンです。
他にもメソッドの中身をごっそりと変えるということもできる(てことは、Strategyパターン?)
delegateの記法
1.まずCのプロトタイプ関数宣言のように書く
public delegate void DelVoid();
public delegate int DelInt(int a);
public delegate string DelStr(string s);
注)ここではあくまで型を作っただけ(structやenumのような新しい型を作るのがdelegate)
2.実際にdelegateを利用して処理する部分を書く
void Method1(DelVoid delvoid){
//処理
delvoid();
}
void Method2(DelInt delint)
{
//処理
delint();
}
void Method3(DelStr delstr)
{
//処理
delstr();
}
ここまでで型の定義とその型を持つ変数の使い方が決まった
外部からの使い方
1.delegate変数の中身を代入
クラス名.DelVoid.delvoid = A;
クラス名.DelInt.delint = B;
クラス名.Delstr.delstr = C;
とすることでMethod1,2,3のdelegete系変数の中身を宣言できる(A、B、Cはそれぞれの型と一致する引数と戻り値を持つ関数)
クラス名の部分にはDelVoidを型宣言したクラス名を書く
ついでにdelegateに対する処理の追加についても説明します。
以下のようにするとAの後に処理Bを追加できます。
クラス名.DelVoid.delvoid = A;
クラス名.DelVoid.delvoid += B;
2.delegateを使う
クラス名.Method1(delvoid);
クラス名.Method2(delint);
クラス名.Method3(delstr);
これで先ほど代入したA,B,Cの内容をMethod1,2,3内で宣言している箇所に貼り付けて使うことができます。
Action
Actionはdelegateの省略表現
Action<引数の型> action = A
のように書く。引数は<string,int>のように「,」で区切ることで複数渡せる
引数がない場合は
Action action = A
でおk
要はActionによって明示的な名前(DelVoidのようなやつ)をつけずに引数と戻り値だけで判断することができる
ただし、Actionは戻り値を返すことができないvoid関数しか作れない
戻り値を返したい場合はFuncを使う
書き方は以下の通り
Func<引数の型(複数可),戻り値> func
例えば
Func<int, string > func
は
string func(int num){
//処理
//最後にstringを返す
}
のような関数のdelegateとなる
引数がない場合は以下のように書く
Func<戻り値> func
例えば、
Func<int> func
は
int func(){
//処理
//最後にintを返す
}
という関数のdelegateとなる
匿名関数
今までAction action = Aと書いていた
これは別にメソッドAを書かなければいけないのでめんどいし、場合によっては可読性が落ちる
これを
Action<int,string> action = delegate(int x, string str){
//処理
}
と書き換えることができる。それだけ
ラムダ式
1行だけの処理であれば、delegate、Actionをより簡単に書ける
1行じゃなくても、書ける。匿名関数の省略記法。だいたいこっちを使うことが多い(2021/01/27 追記)
下のように書く
Action<引数の型> = 引数の変数名 => 処理;
他にも様々な記法があるが、一番簡潔なものを覚えておけば、とりあえずは使える。必要に応じて調べればおk
Event
ほぼdelegateと同じ
違いは外からの代入と実行ができないこと
Delegateとの使い分けは「その関数型変数を内部でしか使わないように限定するかどうか」ということ(カプセル化するかどうかってことか?)
カプセル化する場合はEvent
そうでない場合(interface的な時?)はDelegate
と覚えておけばとりあえず丸い(多分他にも細かい違いがあると思うけど、今は無視!!)
宣言の仕方
例1
public event Action action = null;
例2
public event Action<int> action = num =>{Debug.Log("入力されたのは" + num + "です")};
外部クラスからのアクセス
エラーの例
クラス名.action();
クラス名.action = A
これは外部でdelegateを実行しようとしたり、代入しているのでNG
大丈夫な例
クラス名.action+=A
外からdelegateに処理を付け加えるのはセーフ
まとめ
delegateだけわかってればとりあえずはこの辺の概念はふわっとわかるようになる!!
参考
1.http://kan-kikuchi.hatenablog.com/entry/Delegate
2.https://qiita.com/RyotaMurohoshi/items/740151bd772889cf07de
3.https://hk-ryukyu.club/hideto/archives/49#toc6