delegateの引数省略とLambda
今更何をと思うが、Javaから.NET C#に戻って思うのは、匿名メソッド(匿名デリゲート)が楽しいってことだ。C#2.0から使えるようになったこの機能を使えば、従来はわざわざプレースホルダを用意してやる必要があったイベントハンドラ等を直接且つ直感的に書ける。
Form form = new Form(); form.Shown += delegate(object s, EventArgs e) { form.Text = "fire Shown."; };
まあ、これならばJavaの無名インナークラスの方が便利な位だが、匿名メソッドの便利な(匿名たる所以)は、メソッドの引数を使わないことが自明であれば引数自体を省略してしまえる所だ。※
上のコードは実は以下のようにも書ける。
Form form = new Form(); form.Shown += delegate { form.Text = "fire Shown."; };
ここまで来るとメソッドというよりはブロックと呼びたくなるが、実際にはそうではない。ビルドしたアセンブリのILを見れば解るのだが、引数を省略した匿名メソッドはコンパイラにより正しい引数リストで生成されているのであり、実際に引数が存在しない他のデリゲート型を代入することはできない。
public delegate void AnonHandler(); : AnonHandler anon = delegate {form.Text = "fire Shown.";}; Form form = new Form(); form.Shown += anon; : エラー 1 型 'AnonHandler' を型 'System.EventHandler' に変換できません。
C#3.0では匿名メソッドはラムダ式に進化しており、(匿名メソッドもそのまま使える)最初のコードは以下のように書けたりする。
form.Shown += ((object s, EventArgs e) => form.Text = "fire Shown.";)
これでついにdelegateというキーワードも無くなった。更に、ラムダは型推論ができるので引数リストの型を省略できる。
form.Shown += ((s, e) => form.Text = "fire Shown.";)
なにか別な言語に見えてきた。ならばと思い匿名メソッド同様に引数リスト自体を省略してみた。
form.Shown += ( => form.Text = "fire Shown." );
残念ながらこれはエラー。匿名メソッドにように使っていない引数を省略することはできないらしい。というか、匿名メソッドの引数省略はビルド時に左辺のデリゲートを型を見ることによって生成されるコンパイラの便利機能と捕らえるべきなのだろうか。
正直私にはラムダ式よりも匿名デリゲートの方が読みやすく感じた。慣れの問題だろうか。
※恥ずかしながらこの事を知ったのは割と最近だ。