デリゲートについて勉強してみる

前回の記事ではイベントの基礎について勉強しました。
このとき、「デリゲートの基礎は理解している前提で進めます」と書いておきましたが、「デリゲートってそもそもなんやねん」と首をひねった方もいるかなと思います。
というわけで本日はデリゲートについて勉強してみます。

例のごとく僕はC#歴数ヶ月のペーペーなので、間違っていたら訂正よろですよ。

デリゲートとは

デリゲートを一言で表すと、メソッドを引数で受け取るための機能です。
疑似コードですが以下のような感じですね。

メソッドCalculationは第一引数でint型の数値nを受け取り、第二引数でメソッドを受け取ります。
この受け取ったメソッド(デリゲート)を用いて、int型の数値nをホニャララするわけです。

また、デリゲートは変数やプロパティにも代入可能なので、メソッドの引数に渡す以外のことも可能です。

JavaScriptでも似たようなことができる

少しC#からは話がそれますが、ツクラーの方向けにJavaScriptのことを少し書いておきます。

前の節の説明でピンと来た方もいるかと思いますが、JavaScriptも関数の引数に関数を受け取ることができます。
例えばArray.prototype.forEach(callback[, thisObj])ですね。
配列の各要素に対してcallback関数が呼ばれます。

しかしJavaScriptでは「デリゲート」という用語はありません。
これはなぜでしょうか?

それはズバリ、JavaScriptにおいては関数もオブジェクトの一種だからです。

JavaScriptでは引数にオブジェクトを渡すことができます。
JavaScriptではプリミティブな値を除き、ほぼ全てがオブジェクトです。
もし仮に引数にオブジェクトを渡せないとすると、例えば配列を引数に渡すことができなくなります。
これは非常に困りますよね。
したがって、引数にオブジェクトを渡すことができるのは当然の仕様だと言えます(余談ですが、プリミティブな値がオブジェクトのように振る舞う場合がありますが、それは単にボックス化の機能が働いているだけです)。

そしてJavaScriptにおいては関数もオブジェクトの一種ですから、何の問題もなく引数に関数を渡すことができるのです。
つまり「デリゲート」という特殊な型を作る必要がないので「デリゲート」という用語が存在しないのですね(そもそもJavaScriptに型は7種類しかありません。ES5以前なら6種類)。

デリゲートを使ってみる

それでは実際にデリゲートを使ってみましょう。
今回は「int型の配列を受け取り、その配列内で特定の条件を満たす要素を取得し、各要素に何らかの処理を加え、新しい配列として返す」メソッドを作成してみることにします。
1つのメソッドで2つのことを同時にするのはいかがなものか? と思うきらいもあるでしょうが、まあサンプルコードなので大目に見てください。

まずは前回と同様にコンソールアプリを作成してください。
その後、Mainメソッドの下にDoSomethingメソッドを作成します。

1.int[]型を戻り値として返す
2.第一引数にはint[]型の配列を受け取る
3.第二引数には特定の条件を判定するためのデリケートを受け取る
4.第三引数には何らかの処理をほどこすためのデリケートを受け取る

コードにするとこんな感じです。

第二引数でPredicate<int>というものが目に入りますね。
これは定義済みデリゲートの一種です。
Func<int, int>も同様です。

使われる頻度が高いと思われるデリゲート型は「定義済み」としてSystem名前空間に用意されているので、わざわざ自分でデリゲートを定義する必要がないのですね。
これは便利です。
積極的に使っていきましょう。

見てわかる通り、Predicate<int>はint型の引数を受け取り、bool型の値を返します。
Func<int, int>はint型の値を受け取り、int型の値を返します。

なおstaticにしている理由は、単にMainメソッド内で使いやすいからです。
それ以外の理由はないので、みなさんが実際のコードに組み込むときはstaticにする必要は全くありません。

中身を書いていきましょう。

List<int>型にしているのは、加工した結果として渡される配列の長さが不明だからです。
もっとスマートな方法がある気がしますが、思いつかなかったのでこうしています。
foreach内で受け取った配列を回し、predicateがtrueを返すものだけnewListに追加します。
また、newListに追加するとき、funcメソッドでnumを加工しています。

最終的にはnewListを配列に変換し、戻り値として返しています。

実際に使ってみる

それではDoSomethingメソッドを実際に使ってみましょう。
Mainメソッド内を以下のように変更します。

今回はpredicateとfuncにラムダ式を使いましたが、デリゲートにメソッドを代入し、それを引数として渡しても問題ありません。

結果はこんな感じ。

predicateをいじって条件を変更したり、funcをいじって加工方法を変えてみたりしてみてください。

LINQを使うとラクチン

余談ですが、上記のコードはLINQを使うともっと楽に書けます。
具体的にはこんな感じ(Mainメソッド内に追加してください)。

DoSomethingメソッドを使った「0より小さい値を二倍する」と全く同じ結果が返ってきます。
すなわちWhereメソッドで絞り込みをかけ、Selectメソッドで射影をおこなっています。

ご覧の通り、これらのメソッドの引数にもデリゲートを渡せるので、今回の場合ですとDoSomethingメソッドなんて用意する必要性が全くありませんね。
実際に配列の中身を加工したり絞り込んだりする際にはLINQを使うとよいかなと思います。
詳細は「LINQ 使い方」などでググってください。

実際にどこでデリゲートを使っているのか

ではデリゲートは実際のプログラム上だとどこで使えるのでしょうか?
僕のタイピングゲームですと、例えばワードの絞り込みに用いています。

「上級モード」と「初級モード」では表示させる文字の種類を変える必要があったので、条件(デリゲート)を引数として受け取るメソッドを用意し、その条件に合致する文字だけを表示させるようにしているのですね。
こうすることで、いちいちif文でデータを絞り込む必要がなくなり、ステージごとの文字列の調整が簡単にできるようになるのですね。

まあメソッドの中身はLINQにデリゲートを渡しているだけですが……w

おわりに

いかがでしたでしょーか。
若干不親切気味な記事になりましたが、個人的には知識の整理ができてよかったかなーと思っています。

この記事を読んでもよくわからんかったという方は、デリゲートにしてもイベントにしても、すでに詳しく解説しているサイトは山ほどありますので、それらのサイトも参考にしてみてはいかがでしょーか。

ところで、僕の記事よりもわかりやすい解説サイトが数多くあるのに、なぜ僕は今さらデリゲートやイベントについて長々と語っているのでしょうか?
それはズバリ、先にも書いた通り、自分の知識の再確認と整理のためです。

自分の頭の中にある知識を文章に起こすことによって、どこが理解できていてどこが曖昧なのかが整理できるんですね。
他の人はどうか知りませんが、少なくとも僕はそうなのです。

ちゅーわけでこういう系の記事の大部分は誰かのためと言うよりは、僕自信のために書いています。
わりと効果があると思っているので、「知識の整理してみたいなー」なんて思っている方は、試しに解説文をしたためてみてはいかがでしょーか。

ほなそんな感じでまた。

フォローする

『デリゲートについて勉強してみる』へのコメント

  1. 名前:Johnk746 投稿日:2018/01/05(金) 14:15:57 ID:a2af8b030

    Hey are using WordPress for your blog platform? I’m new to the blog world but I’m trying to get started and set up my own. Do you require any coding knowledge to make your own blog? Any help would be really appreciated! daeecdfedfdc

    • 名前:ツミオ 投稿日:2018/01/05(金) 17:37:30 ID:372a1cb9c

      Hi,Johnk746.

      Yes.This blog is created by WordPress.
      Not necessarily need coding knowledge.
      You can create blog easily by using WordPress.