C#のActionクラスとFuncクラスを理解する
photo credit: ARMLE Action ! via photopin (license)
C#でメソッドや関数を表す 2つのクラス Action と Func について説明します (全くの余談ですが、C言語的には関数ポインタ、JavaScript的にはFunctionオブジェクトに相当するものです)。
2つの違いは戻り値の有無。戻り値なしが Action で、戻り値ありが Func です。
どちらもジェネリックパラメータとして関数のパラメータリストを指定できるジェネリッククラスです。 ジェネリックパラメータを指定しない場合は引数無しの関数を表すことになります。
以降、それぞれの詳細な使い方について説明します。
※ この記事は、もともと「C#のラムダ式はAction・Funcと一緒に理解を深めるとヨロシイようで」で書いていた内容です。長ったらしいのでこちらに独立させました。
Action クラス
Actionクラスは「戻り値のない処理を記述するためのクラス」です。
Action
だけなら引数もなし。
// 引数無しのActionをラムダ式で生成
Action action = () => {
foo.bar();
};
引数が必要ならAction<引数の型リスト>
を使用します。
例えば、
//引数を取るActionをラムダ式で生成 Action<string, int> action = (name, age) => { Console.WriteLine(string.Format( "{0} is {1} years old.", name, age)); };
てな感じです。
このように、インスタンスを作ること自体は難しいことではありませんが、誰かが作った既存のメソッドがActionクラスの引数を要求している場合、この逆の考え方、つまり、そのメソッドの定義を見て「何を与えればよいのか」をきちんと理解する必要がありますね。
覚えること ☞「Actionクラスは戻り値のない関数。ジェネリックパラメータは引数リストの型を表しています。」
Func<TResult>
クラス
Funcは戻り値があるメソッドを表します。戻り値がboolで、引数のないFuncは、以下のように記述します。
//戻り値がboolのFuncをラムダで生成 Func<bool> func = () => { return true; };
引数が必要な場合は、Actionと同じくジェネリックパラメータで型を指定します。
//戻り値がboolで引数付きのFuncをラムダで生成 Func<string, int, bool> isAround50 = (name, age) => Console.WriteLine(string.Format( "{0} is {1} years old.", name, age)); if(45 <= age && age < 55) { return true; } return false; };
これがたとえば、メソッドのパラメータだとしても、考え方は同じで、以下のように書くわけです。
Task<bool> task = new Task<bool>(() => { return execute.Invoke(); }); chainedTask._task.Start();
Lambda式は、ActionかFuncクラスのインスタンスを生成していると考えられます。 このため、Lambda式だけを見て、どちらの型なのか推測するクセをつけておくと、混乱が少なくなる気がします。 ようするにしっかり理解しておきましょうということですが。
上の例では、Task<bool>
のコンストラクタの第一引数の型は Func<bool>
だと推測できますね。
そのほか無駄話など
Func<void>
ではダメなんですか?
個人的な好みとしてですが、戻り値の有無でクラスを分けずに、Func<void>
を認めて、Action
クラスはなくてよいでしょと思っています。
しかし、そもそもジェネリックの型パラメータにvoid
は無理なのかもしれませんね。
VBでもFunctionとSubに分かれているし、マイクロソフトさんは昔から分けたい派なのかと思っていたけど、言語的制約なのかもしれません。