C#のラムダ式はAction・Funcと一緒に理解する
photo credit: Imperial Shuttle via photopin (license)
何事も表面的な丸暗記でなく、基礎からキッチリ理解するほうが結果的には早道ですね。
C#のラムダ式に関しては、ActionクラスととFuncクラスをしっかり理解しておけば、それほど難しくはないはずです。
C#のActionクラスは、戻り値のないメソッドを表すクラスで、Funcは戻り値のあるメソッドです。
どちらもジェネリッククラスで、Actionは引数リスト、Funcは戻り値の型と引数リストをジェネリックパラメータで指定します。
Actionはメソッドの動作内容に注目した名前であり、Funcは数学的な関数として評価値を持つ(=戻り値がある)ということですね。
この2つのクラスについては以下のページで詳しく書いていますので参照してください。
C#のラムダ式
ラムダ式の記法的には、以下のような感じ。引数がひとつなら丸かっこは不要とか、波かっこの中身が単一の文なら(複文でないなら)波かっこは要らないとか、いろいろあるんだけど、基本はこちらでOKです。
(name,age) => { Console.WriteLine( string.Format( "{0} is {1} years old", name, age)); };
型とか指定されていないし、戻り値ってありなの?無しなの?どうでもいいの?てな具合に、全く情緒が安定しない代物ですが・・・
ラムダ式は即時呼び出しできません
JavaScript的にラムダ式を直接呼び出そうとしてエラーになって「何がダメなの?」と混乱しました。 「C#のラムダ式ってJavaScriptの無名関数と一緒でしょ?関数オブジェクトそのものでしょ?」的な思い込みがあったんですね。
でもそれは間違い。C#のラムダ式は、それそのものを呼び出すことができません。
ラムダ式というランタイムオブジェクトは無い
C#のラムダ式は、ActionクラスかFuncクラスのインスタンスを生成するためのものなんですよね。
そもそもこれまで、ラムダ式を見たとき「型が明示されていないのに、どうコンパイルされて実行されているのだ?」と思っていたのですが、正しく動いているコードなら、一見欠落しているように見える引数や戻り値の型情報は、一緒に使われている Action
や Func
、またはデリゲートで推測可能になってるはず。
ラムダ式はそれらのオブジェクトを生成するために使われるのですが、ランタイムに「ラムダ式」というオブジェクトとして存在しているわけではないということです。
単なる記法、シンタックス・シュガーです
つまり「ラムダ式は、デリゲートや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のラムダ式は無名クラスのインスタンスなんですね。 言語によって扱いが異なるのは興味深いがヤヤコシイ。