PHP

従来はWeb開発でrubyを積極的に使ってきました。PHPに最初に触れた頃にはオブジェクト指向でプログラミングできませんでしたので、当時はあまり魅力のない言語と認識していました。去年からPHPを使用されてきたSI様とのおつきあいが始まったので、PHPに積極的に取り組むことになりました。バージョンはPHP 5.2.14です。このバージョンになるとオブジェクト指向で組むのに困らないほどに一通りの機能が揃っています。さらに5.3.Xではクロージャの機能が加わり、着実に機能改善されているようです。

PHPの印象は堅実で飛び道具が少ないことです。JavaScriptやrubyでは組み込みクラスにメンバーを追加できますが、PHPではそのようなことはできません。ライブラリは最初から幅広い機能が含まれていて大抵のことに困らないのも良いところです。データベースのNumber型に相当する型(二進化十進)が組み込みでないのが障害になるかも知れません。

以前から有る参照の機能は変数のエイリアスとして使え、便利なものと思います。オブジェクトの代入はruby, C#などの言語と同様に参照の代入になりましたが変数の参照とは意味が違うので少し誤解しやすいところです。配列の代入は従来通り内容のコピーになります。といっても実際には内容を変更したときに初めてコピーされる(コピーオンライト)ので、単純な代入でパフォーマンスが落ちることはありません。

テンプレートエンジンやO/Rマッパーなどrubyで構築したライブラリを移植しましたので、rubyと同様開発を効率化できるものと考えています。

NetBeans

今までPHPを使った開発では、秀丸を使用してきました。秀丸などのテキストエディタでは補完精度が劣る・デバッガないなどIDEに比べると機能的に不便です。そこでEclipseをインストールして使っては見るのですが、動作が遅くタイピングに追いつかないので継続的に使うことはありませんでした。

去年の秋頃からNetBeans6.9.1を使うようになりました。動作の速さは秀丸に比べると劣りますがEclipseと比べればきびきびと動いてくれました。特に便利と思ったことは、

状況に応じた識別子の補完、オブジェクト指向で組んでいると補完候補がクラスのメンバーに絞られるので間違いが少なくなる

  • PHPDoc・JsDocなどのドキュメントの雛形を作成してくれ、関数・クラスなどを使用した箇所でドキュメントがポップアップしてくれる
  • 関数・クラスなどの定義にジャンプできる
  • リモートデバッグできる
  • リファクタリング支援機能が備わっている
  • 単体テストが統合されている
  • バージョン管理システムと連係できる
  • コードテンプレート(スニペット)が使える

などです。識別子の補完では動的型付け言語にもかかわらず、たいていの場合において正確に補完してくれました。リモートデバッグは今まで使ったことがありませんでした。Web開発では処理途中の変数の内容を調べるためにvar_dumpを挿入するなどで対応していましたが、リモートデバッグを使えばソースに変更を加えることなくデバッグできますので効率が上がります。

少し不便なので設定を変えて使っているところがあります。それは自動ポップアップを無効にしていることです。下記URLに報告しましたが、自動ポップアップを有効にしておくとコードテンプレートが使えなくなってしまうのです。自動ポップアップを無効にしても<Ctrl>-<Space>を押せば呼び出せるのでたいした不便はありません。

JavaScriptではコード補完が不正確だったり、ソース整形が不正確だったりでPHP対応に比べて劣っています。

NetBeansに慣れると秀丸に戻れなくなりました。

参照

C#でnullチェック

1
2
3
4
5
6
7
8
9
public Control GetParentParent(Control control)
{
if (control != null) {
if (control.Parent != null) {
return control.Parent.Parent;
}
}
return null;
}

インスタンスメソッドを呼び出すときに気をつけないといけないことのひとつにオブジェクトがnullかどうかをチェックしなければならないことがあります。チェックを怠るとNullReferenceExceptionが発生してしまいます。しかし、nullチェックをするとコード行数が増え、見通しが悪くなります。

例えばコントロールの親の親を取得するメソッドGetParentParentはおおよそ右のコードになります。親の親に該当するものがなければnullを返します。オブジェクトをたくさんたどればそれだけ条件分岐が増えていきます。

1
2
3
4
5
6
7
8
9
public static void _<T>(this T obj, Action<T> action)
{
if (obj != null) action(obj);
}

public static TResult _<TSource, TResult>(this TSource obj, Func<TSource, TResult> func)
{
return obj == null ? default(TResult) : func(obj);
}

これを簡単に書くためにちょっとしたヘルパーメソッドを作成します。コードを短くするために名前をアンダースコアにしてあります。次の1つめの拡張メソッドはobjがnullの場合actionを実行しません。ジェネリックを使っていますのでactionとつじつまが合っていればどんな型でも渡せます。2つめのメソッドは値を返すものでobjがnullの場合funcを実行しないで返値の型TResultの既定値を返します。TResultがValueTypeでなければnullを返します。インスタンスメソッドの場合はオブジェクトがnullの場合にメソッド呼び出しができませんが、拡張メソッドは静的メソッドを第1引数のメソッド呼び出し形式で呼び出せるシンタックスシュガーに過ぎませんので引数objにnullが渡されてもエラーにならないのです。

1
2
3
4
public Control GetParentParent(Control control)
{
return control._(c => c.Parent)._(c => c.Parent);
}

このヘルパーメソッドを利用するとGetParentParentはLINQ同様メソッドチェーン1行で記述できます。

HaskellにはMaybeモナド、Delphi Prismにはコロン演算子がありますが、C#でも上記のヘルパーメソッドで簡潔に書けるようになります。

Visual Basicではアンダースコアが行連結文字になっていますので別の名前を付けなければなりません。

1
2
3
4
public Control GetParentParent(Control control)
{
return control?.Parent?.Parent;
}

C# 6.0 (Visual Studio 2015) では Null条件演算子(?.)が導入されました。

参照

Visual BasicからC#に移行した結果

1つのプロジェクトをVisual BasicからC#に移行しました。実際移行してみて感じたメリット・デメリットがありました。

C#の方が優れていた部分として、

ラムダ式によってAdapterが不要になった
私はコマンドを受け付ける部分をコマンドパターンによって使い回しするようにしています。Visual Basicではルーチンをコマンドにラップするクラスを記述していました。ラムダ式によって簡単にコマンド化することができるのでこれら多くのクラスが不要になりました。コーディングの自由度が高く、SEの設計センスがより問われると思います。
コンパイル速度が速い
体感で2倍程度。
リファクタリングの種類が豊富
名前の変更だけでなく、フィールドのカプセル化・パラメータの順序変更など多くのリファクタリングが利用できる。

C#の使いにくい部分は、

括弧の対応が分かりにくい
if,swithなどの制御構造を追加するなどインデントが崩れると右括弧がどれに対応しているのかわからなくなる。End Subなどでメリハリが付いているVisual Basicの方がよい。
型名が省略できない
Withもなくusingに型名が指定ないので、他のオブジェクトのメソッドを呼ぶときは型名(or オブジェクト変数名).メソッド名の形式で呼ばなければならず、細かいところで記述量が多い。コンパイル速度が速い原因は型名を省略できないことかも知れないので善し悪しは微妙なところ。

また、null(Nothing)の扱いには違いがあり、Visual Basicではイコールで比較すると長さ0の文字列とNothingを同一視しますがC#では区別します。

総合的にはC#に移行して良かったと思います。その一番大きなファクターはラムダ式です。

参照

Linqと遅延実行のイメージ

Linqと遅延実行のイメージ

Linqでは要素を取得する処理は本当にその要素が必要なってはじめて実行されます。これを遅延実行と呼びます。Linqによる遅延実行は、複数のデバイスが通信によってデータを要求・応答している様子をイメージをすればよいと思います。

Linqと遅延実行のイメージ

デバイス1がデータを発生します。デバイス2がデータを中間処理します。デバイス3がデータを最終処理します。処理はデバイス3がデバイス2に対してデータを要求することで開始します。デバイス2は要求を受けてデバイス1に対してデータを要求します。デバイス1はデバイス2に対して応答を返します。デバイス2は応答を受けたらデバイス3に応答を返します。これが繰り返されるのです。

デバイス1やデバイス2は、図中自分より右のデバイスにデータを要求されて初めて応答します。遅延実行はこのような仕組みで働きます。

遅延実行で省メモリ

リスト1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LinqLazy
{
static class EnumerableExtentions
{
public static void Each<T>(this IEnumerable<T> source, Action<T> func)
{
foreach (var elm in source) {
func(elm);
}
}
}

class Program
{

public static IEnumerable<int> Generator()
{
for(var i=0; i<10000; i++){
yield return i;
}
}

static void Main(string[] args)
{
Generator().Where(x => x > 2).Each(i => Console.WriteLine(i));
Console.ReadKey();
}
}
}
Linqと遅延実行のイメージ

リスト1の例はGeneratorが0から10000までの整数を発生させます。WhereGeneratorから渡された要素が2より大きいものだけを通すようにフィルタリングします。Each内で結果を表示します。遅延実行の様子はシーケンス図で確認してください。MoveNext Currentがデータの要求、yield returnが応答です。

もしGeneratorのすべての処理が終わってからWhereの処理に移るならば、int約10,000個分のバッファが必要になります。しかし遅延実行を行っているために1個分のバッファで済むのです。この場合遅延実行のメリットは省メモリです。

遅延実行で無限Generator

リスト2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LinqLazy
{
static class EnumerableExtentions
{
public static void Each<T>(this IEnumerable<T> source, Action<T> func)
{
foreach (var elm in source) {
func(elm);
}
}
}

class Program
{
public static IEnumerable<int> Generator()
{
//無限に整数をカウントアップする
for(var i=0; ; i++){
yield return i;
}
}

static void Main(string[] args)
{
Generator().TakeWhile(x => x < 3).Each(i => Console.WriteLine(i));
Console.ReadKey();
}
}
}

リスト2ではGeneratorが無限に整数を発生させています。TakeWhileGeneratorから渡された要素が3より小さい間だけ通すようにフィルタリングします。無限ループにならずに終了するのは0,1,2,3だけが処理されるからです。無限ループにならずに実行できるのは遅延実行のおかげです。

TakeWhileWhereに変えると遅延実行はされるものの処理は続き無限ループになります。無限に要素を返すジェネレータはTakeWhileのように処理を打ち切るフィルタと組み合わせて使う必要があります。

遅延実行されない場合

Linqがいつも遅延実行されるわけではありません。Max,Min,OrderByなどは原理的にすべての要素が揃わないと最初の要素すら決まりませんのでこのようなクエリ演算子は遅延実行されません(マイクロソフトでは集中評価と呼んでいるようです)。

Linqで言う遅延実行はメソッド呼び出し時の引数の遅延評価とは意味が異なります。Linqに限らず引数にデリゲート・ラムダ式を指定して評価を呼び出し後に行うことを遅延評価と呼んでいます。遅延実行のためには遅延評価も必要です。

Delphi 2009

Delphi 2009が発売されました。

NetFrameworkではなくWindowsネイティブ用です。2007までと大きく異なりVCLのUnicode化、ジェネリック・無名メソッドの導入と久々の意欲作となっています。

特にUnicodeについてはよく使われるようになったので、これが今まで扱えなかったことはDelphiを使う上で大きな足かせになっていました。

Visual Studio 2008に比べ動作はきびきびしています。Visual Studio+NetFrameworkは開発・実行共に動作がところどころ遅いのですが、Delphiではそのようなことを感じさせません。

過去にDelphiで作ったコレクションを扱うライブラリを早速ジェネリックに置き換えるとコーディング量が1/3程度に減った上に型安全になりました。但しジェネリックに関してはIDEがまだまだブラシアップできておらずエラーではないのにエラーインサイトでエラーを報告するなど不具合が残っています。今後ホットフィックスで修正されるのを待つことになりそうです。

IDEではユニットテスト機能(DUnit)が統合されています。テスト対象クラスから自動的にテストスケルトンを作ってくれます。テスト内容はもちろん自分で記述しなければなりません。

コントロールの貼り付けは非常にスムーズで型名で検索の機能を使うとパレットを探し回る必要がありません。フォームデザイナとコードエディタの切り替えも一瞬です。

言語仕様を比較してみました。

項目 Delphi 2009 VB 2008 C# 3.0 コメント
ジェネリック DelphiはIDEに不具合が残る
無名メソッド × C#ではラムダ式で簡易記述できる
ミックスイン × × DelphiはImplements句によるミックスイン、Rubyと比べて記述は煩雑
コルーチン × DelphiはFiberで対応、言語仕様に機能はない。C#はyieldで列挙に限りコルーチンを使える。
ガーベジコレクション × Delphiでは文字列・インタフェースは参照カウント方式、Componentは親を解放すると所有される子が解放される。それ以外は明示的に解放。
型推論 ×
仮想コンストラクタ × × Abstract Factoryを単純に記述できる
拡張メソッド Delphiはクラスヘルパーを使用、制限大
遅延バインディング × DelphiではVariantを使う。

IEnumerableによるフィルタ(C#)

Visual Studio 2008から導入された標準クエリ演算子を利用するスタイルでは、IEnumerableを返すメソッドから始まり、中間ではIEnumerableを受け取ってIEnumerableを返すメソッド、最後はIEnumerableを受け取って最終処理するメソッドを数珠(じゅず)繋ぎにします。IEnumerableを受け取るとは引数で受け取ることを考えてしまいますが、そうではなくIEnumerableのメソッドとして実装することでフィルタ処理を左から右に順にドットで結びつけて表現できるので、処理順序とプログラム記述順序が一致します。

各フィルタ内部ではyieldを使ってコンテキストスイッチを行い次のフィルタにデータを渡す処理を内部で繰り返しています。そのためフィルタを記述するのに繰り返しをその都度記述する必要はありません。

IEnumerableによるフィルタ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

namespace ToUpper
{
static class EnumerableExtentions {
public static void Each<T>(this IEnumerable<T> source, Action<T> func)
{
foreach (var elm in source) {
func(elm);
}
}

public static IEnumerable<T> Map<T>(this IEnumerable<T> source, Func<T, T> func)
{
foreach (var elm in source) {
yield return func(elm);
}
yield break;
}
}

class Program
{
public static IEnumerable<string> FileReader()
{
string line;
while ((line = Console.ReadLine()) != null) {
yield return line;
}
yield break;
}

static void Main(string[] args)
{
FileReader().Map(line => line.ToUpper()).Each(line => Console.WriteLine(line));
}
}
}

NetFrameworkではSelect,Whereなどのメソッドが用意されていますがこれらはどのように作ればよいのでしょう。IEnumerableを利用するスタイルで使えるEachMapというメソッドを作ってみます。EachIEnumerableを受け取って、引数で指定したメソッドに各要素を順に渡すメソッド、Mapは、指定したメソッドに各要素を順に渡すだけでなくそのメソッドが何らかの変換を行って値を返します。IEnumerableはNetframeworkに用意されている型ですが、作り付けの型に新たなメソッドを追加しなければならないことになります。また、IEnumerableはクラスではなくインタフェースですから通常は実装を持たないはずです。なおさらメソッドを実装することが難しく思えます。2008からは拡張メソッドによってそれが可能になっています。

Each,Mapは共に拡張メソッドとして実装しました。Eachの第2引数funcは各要素を受け取って処理するメソッドを指定します。Actionは引数を一つ受け取り、値を返さないメソッドを表します。一方Mapでは値を返しますから、Func T>となっています。Funcとは一つの引数T1を受け取り、T2を返すメソッドを表します。ここでは両方Tですから引数と同じ型の値を返すのです。

使用例は、標準入力を順に読み取り、大文字にして標準出力に書き込むコンソールプログラムです。英文を入力してEnterを押せば次の行に大文字に変換された文字列が表示されます。終了させるにはcontrol-Zに続いてEnterを押します。

FileReaderは標準入力を1行ずつ読み取るメソッドです。FileReaderは次のMapに結果を渡します。Mapの引数には大文字変換を行うラムダ式が指定されています。MapはさらにEachに結果を渡します。Eachの引数には渡された文字列を表示するラムダ式が指定されています。

これを発展させれば多段階の処理が必要なフィルタを実現することもできます。

この例では頻繁にyieldが呼ばれます。yieldのパフォーマンスは下記ページを参考にしてください。

参照

ラムダ式によるフォーム生成(C#)

Visual StudioのIDEでWindowsフォームを作ると、System.Windows.Forms.Formを継承したクラスが作成されます。今回はIDEに頼らないでラムダ式を使ってコーディングしてみます。

ラムダ式によるフォーム生成
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
private void ShowForm2() {
var form = new Form();
var nextFormButton = new Button();
var closeButton = new Button();

form.Text = "form2";

nextFormButton.Location=new Point(30, 50);
nextFormButton.Text = "次へ";
nextFormButton.Click += (sender, e) => ShowForm3();

closeButton.Location = new Point(120, 50);
closeButton.Text = "閉じる";
closeButton.Click += (sender, e) => form.Close();

form.Controls.Add(nextFormButton);
form.Controls.Add(closeButton);
form.Show();
}

private void ShowForm3() {
var form = new Form();
var textbox1 = new TextBox();
var textbox2 = new TextBox();
var closeButton = new Button();

form.Text = "form3";
textbox1.Location = new Point(10, 10);
textbox1.Leave += (sender, e) => textbox2.Text = textbox1.Text;
//textbox1 = null;

textbox2.Location = new Point(10, 30);

closeButton.Location = new Point(30, 50);
closeButton.Text = "閉じる";
closeButton.Click += (sender, e) => form.Close();

form.Controls.Add(textbox1);
form.Controls.Add(textbox2);
form.Controls.Add(closeButton);
form.Show();
}

ShowForm2は「次へ」「閉じる」ボタンがついたフォームを作成・表示するメソッドです。次へのボタンでShowForm3を実行します。ShowForm3はテキストボックスが2つと「閉じる」ボタンがついたフォームを作成・表示するメソッドです。上側のテキストボックスに文字を入力してTab移動などを行うと、同じ内容が下のテキストボックスにもコピーされます。

このコードの特長は一つのメソッドでフォームの生成とイベントハンドラをすべて記述できていること、また、フォームの作成に伴って新たなクラスを全く必要としていないことです。アプリケーション全体を新たなクラスなしに作ることもできそうな気がします。しかし実際にそのようにすれば設計上いろいろな無理が出てきそうです。フォームを作成する場面ではIDEが使えないこの手法は採用されることはないと思いますが、Adapterなどその他の場面はラムダ式を使って簡潔に記述できる場面もあります。

このように2つのアプローチが存在することとを理解した上で、ラムダ式が向くところ、オブジェクト指向が向くところを適宜選択しながら設計を行うことがこれからの開発スタイルになりそうです。

補足

ラムダ式を使う上での注意点があります。ShowForm3textBox1LeaveイベントでtextBox2.Text=textBox1.Textが実行されます。

この場合のtextBox1textBox2などのラムダ式の外で宣言された変数を自由変数、sendereなどのラムダ式の引数となっている変数を束縛変数と呼びます。

これが実行されるのはShowForm3メソッドが完了してtextBox1からフォーカスが抜けたときですから、それまでに自由変数textBox1に別のものを代入すると不正な処理が実行されます。例えばコード中のtextbox1
= null
のコメントを外すとLeave時に例外が発生ます。つまり自由変数は使い回しすることができないのです。特にループ中のラムダ式ではその点に気を付ける必要があります。具体的にはループ内で自由変数を宣言して使います。

自由変数が実際に使用される時点では既に変数スコープを抜けてしまっていますがそれについては内部的に保持されているので問題ありません。

ラムダ式、Adapterへの応用(C#)

アプリケーション開発では通常NetFrameworkなどの既製のライブラリを使用します。既製のライブラリは汎用的に作ってありますのでこれを開発しようとするシステムに合わせてより使いやすい中間的なライブラリを開発することがしばしば必要になります。例えばデータベース応用システムをNetFramworkで開発する場合、BindingSourceDataTableにバインドすることが圧倒的に多いわけのですが、BindingSourceは汎用的に作ってあるため使いにくいところがあります。そこでDataTableしかバインドできないけれどもそれ専用に使いやすいメソッドを備えたクラスを新たに開発することもこれに該当します。提供される機能と必要とする機能が一致しないためその差を埋める必要があるわけです。

粒度の小さい例ではイベントハンドラの引数があります。イベントを発生させたオブジェクトがsenderに付加的な情報がeに渡されますが、これらを使用することは私の場合あまりなくてむしろ別の引数が欲しいことが多いのです。電卓のようなアプリケーションを考えると数字ボタンはたくさんありますが0から9まで行っている処理はそれぞれの数値入力を受け取ることです。sendereが不要で入力した数値が必要となります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//IDEでイベントハンドラを作成
private void button1_Click(object sender, EventArgs e) {
Console.WriteLine('1');
}

//Adapterクラス
private class Adapter {
char _letter;

public Adapter(char letter) {
_letter = letter;
}

public void Handler(object sender, EventArgs e) {
Console.WriteLine(_letter);
}
}

//ラムダ式を使ったアダプタ
private EventHandler AdapterByLambda(char letter) {
return (sender, e) => Console.WriteLine(letter);
}

private void Form1_Load(object sender, EventArgs e) {
button2.Click += new Adapter('2').Handler; //Adapterクラスを使用
button3.Click += AdapterByLambda('3'); //ラムダ式を使ったアダプタを使用
button4.Click += (sender1, e1) => Console.WriteLine('4'); //その場でラムダ式を記述
}

右記サンプルコードを試すにはWindows Formを作成して、ボタンを4つ貼り付けてください。実行結果は出力ウィンドウで確認します。

IDEでは簡単にイベントハンドラを作れますので10個のイベントハンドラを作ることも一つの方法として考えられます。Button1がこの方法でイベントハンドラを設定しています。

別の方法はAdapterクラスを作ることです。数値をクラスのメンバー_letterに代入しておき、EventHandler形式のメソッドとしてイベントハンドラを取り出します。Button2がこの方法でイベントハンドラを設定しています。

ラムダ式を使うとアダプタを簡単に記述できます。Button3がこの方法でイベントハンドラを設定しています。リスト中のAdapterByLambdaを見てください。ラムダ式はメソッド(ポインタ)を返す式で=>の左辺が引数、右辺がメソッド本体を表します。各引数の型は型推論により指定不要です。メソッド本体が複文になるときは{}で囲みます。AdapterByLambdaを呼び出したときにラムダ式が返すメソッドが実行されるわけではありません。AdapterByLambdaの引数letterはラムダ式実行時にも保持されています。

アダプタがこれだけ単純になるのならと、Button4はアダプタを使わないで直接ラムダ式を記述しています。アダプタを他で再利用しないならこのような簡便な方法も使えます。

ラムダ式、Decoratorへの応用(C#)

時間の掛かる計算処理が終わったときに終了メッセージを表示したり、例外処理やトランザクションの様に特定のコンテキストで、何らかの処理を行いたいことがあります。これらの事前処理や事後処理をラムダ式や匿名メソッドを使わないで実現することは面倒なものです。終了メッセージの例では計算処理本体が既にメソッドになっている場合に、計算処理の後に終了メッセージを表示するだけのメソッドを新たに書かなければなりません。例外処理の例では汎用的な例外処理で済む場合にもTry
Catchをその都度記述するか、汎用的な例外処理を行うメソッドを作ったにしても、Try Catchの内側をメソッドとして独立させなければなりません。

このような場合にラムダ式を使うとメソッドの数が減り、一続きの処理なのに記述箇所が散らばってしまうのを避けることできます。

Form1のコード
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private void HeavyOperation() {
Console.WriteLine("時間の掛かる処理");
System.Threading.Thread.Sleep(1000);
}

private void Form1_Load(object sender, EventArgs e) {
button1.Click += Lib.ToEventHandler(() => MessageBox.Show("button1.Click"));
button2.Click += Lib.ToEventHandler(() => {
HeavyOperation();
MessageBox.Show("時間のかかる処理を実行しました.");
});
button3.Click += Lib.ToEventHandler(
Lib.GetExceptionTrapAction(() => {
var a = 0;
var b = 0;
var c = a / b;
})
);
<span class=code-comment>/*
button4.Click += Lib.ToEventHandler(Lib.ShowForm2AndExecute(() => {
HeavyOperation();
MessageBox.Show("時間のかかる処理を実行しました.");
}));
*/</span>
}

サンプルコードはAdapterの例と同様にフォームのロード時に各ボタンのイベントハンドラを設定しています。

Button1はクリックしたらメッセージを表示するだけの単純な処理です。Lib.ToEventHandlerは引数・戻り値のないメソッドをEventHandlerに変換するライブラリのメソッドです。

Button2は時間の掛かる計算処理が終わったときに終了メッセージを表示する例です。HeavyOperationは処理本体です。HeavyOperationとメッセージ表示を順次実行する短い処理をラムダ式で表現しています。
Button3はゼロでの割り算により例外が発生する処理です。Lib.GetExceptionTrapActionは処理を汎用的な例外処理コンテキストの下で実行するメソッドです。この場合はTry
Catchの内側の処理をラムダ式で指定しています。

印刷処理などでは印刷前にタイトルを入力したり、データ抽出などの指定を行うために別フォームを表示することがあると思います。指定用のフォームをいろんな帳票に使い回すケースもよくあります。そんなとき起動したフォームから指定用のフォームの「実行」ボタンが押されたときの処理を渡す必要があります。これを簡潔に書くためにラムダ式を使います。