銀の弾丸

プログラミングに関して、いろいろ書き残していければと思っております。

C#のActionクラスとFuncクラスを理解する

f:id:takamints:20181112124832j:plain
photo credit: ARMLE Action ! via photopin (license)

C#でメソッドや関数を表す 2つのクラス Action と Func について説明します (全くの余談ですが、C言語的には関数ポインタ、JavaScript的にはFunctionオブジェクトに相当するものです)。

2つの違いは戻り値の有無。戻り値なしが Action で、戻り値ありが Func です。

どちらもジェネリックパラメータとして関数のパラメータリストを指定できるジェネリッククラスです。 ジェネリックパラメータを指定しない場合は引数無しの関数を表すことになります。

以降、それぞれの詳細な使い方について説明します。

※ この記事は、もともと「C#のラムダ式はAction・Funcと一緒に理解を深めるとヨロシイようで」で書いていた内容です。長ったらしいのでこちらに独立させました。

目次

Action クラス

ACTION
ACTION
posted with amazlet at 16.09.22
B’z
VERMILLION RECORDS(J)(M) (2007-12-05)
売り上げランキング: 22,294

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>クラス

グレイト・ヒッツ
グレイト・ヒッツ
posted with amazlet at 16.09.22
T.レックス
インペリアルレコード (2005-05-25)
売り上げランキング: 3,577

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に分かれているし、マイクロソフトさんは昔から分けたい派なのかと思っていたけど、言語的制約なのかもしれません。