銀の弾丸

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

C#のラムダ式はAction・Funcと一緒に理解する

f:id:takamints:20160922160154p:plain
photo credit: Imperial Shuttle via photopin (license)

C#ラムダ式をスッキリ理解する方法です。

何事も表面的な丸暗記でなく、基礎からキッチリ理解するほうが結果的には早道ですよ。

C#ラムダ式に関しては、ActionクラスととFuncクラスをしっかり理解しておけば、それほど難しくはないはずです。

いくつになってもお勉強ですね。

C#のActionクラスは、戻り値のないメソッドを表すクラスで、Funcは戻り値のあるメソッドです。 どちらもジェネリッククラスで、Actionは引数リスト、Funcは戻り値の型と引数リストをジェネリックパラメータで指定します。 Actionはメソッドの動作内容に注目した名前であり、Funcは数学的な関数として評価値を持つ(=戻り値がある)ということですね。 この2つのクラスについては以下のページで詳しく書いていますので参照してください。

takamints.hatenablog.jp

目次

C#ラムダ式

C#逆引きレシピ
C#逆引きレシピ
posted with amazlet at 16.09.22
arton
翔泳社
売り上げランキング: 97,981

ラムダ式の記法的には、以下のような感じ。引数がひとつなら丸かっこは不要とか、波かっこの中身が単一の文なら(複文でないなら)波かっこは要らないとか、いろいろあるんだけど、基本はこちらでOKです。

(name,age) => {
    Console.WriteLine(
        string.Format(
            "{0} is {1} years old",
            name, age));
};

型とか指定されていないし、戻り値ってありなの?無しなの?どうでもいいの?てな具合に、全く情緒が安定しない代物ですが・・・

ラムダ式は無名関数ではありません

JavaScriptの即時関数的にラムダ式を直接呼び出そうとしてエラーになって「何がダメなの?」と混乱しました。 結構JavaScriptに慣れ親しんでいるので「C#ラムダ式ってJavaScriptの無名関数と一緒でしょ?関数オブジェクトそのものでしょ?」的な思い込みがあったんですね。

でもそれ間違いでした。 C#ラムダ式は無名関数ではありえないのです。つまり、・・・

ラムダ式というランタイムオブジェクトは無い

結局、ラムダ式を問題なく自由に使えるようになったのは、ActionクラスとFuncクラスを理解して、一緒に使うものだと認識してからでした。

それまでは、ラムダ式単体で見て「型が明示されていないのに、どうコンパイルされて実行されているのだ?」と不審に思っていました。 しかし、その、引数の数や型、戻り値の型など、欠落している情報は一緒に使われている ActionFunc、またはデリゲートで明示されているってことですね。

ラムダ式はそれらのオブジェクトを生成するために使われるんだけど、ランタイムに何らかのオブジェクトとして存在しているわけではないということです。

単なる記法、シンタックス・シュガーです

つまり「ラムダ式は、デリゲートやActionやFuncを記述するためのシンタックス・シュガー」であって、それ自体はオブジェクトでもなんでもなく「単なる記法」というわけです。

その証拠に、JavaScriptの即時関数(以下)のようなコードは、C#ラムダ式では実装不可です。コンパイルが通りません。

//JavaScriptの即時関数呼び出し
var a = 1;
(function(b) {
    a += b;
}(2));
console.log("a:", a);// "a: 3"

C#では、一旦Actionインスタンスを作ってからでないと実行できません。

なぜかというと、ラムダ式だけでは、引数の型が特定できないからなんですね。

以下のコードでは、Actionクラスによって、第一引数がintであることが明示されている(あえて似せて書いていますので無駄に丸かっこが付いています)ので実行可能となるわけです。

int a = 1;
(new Action<int>(b => {
    a += b;
})(2));
Console.WriteLine(string.Format("a: {0}", a));

JavaScriptでの無名関数は関数オブジェクトですが、C#ラムダ式に直接対応するオブジェクトはないってことです。 ちなみにJavaラムダ式は無名クラスのインスタンスなんですね。 言語によって扱いが異なるのは興味深いがヤヤコシイ。