Visual Studio 2008 C#で開発効率アップ!

第2回 コード比較で理解するC#3.0の新機能(2)

この記事を読むのに必要な時間:およそ 3 分

はじめに

前回は,C#3.0とC#2.0のコードを比較をすることで,比較的容易に理解できる新機能についてを中心にご説明しました。 今回は,ラムダ式についてご説明して行きたいと思います。

前回に比べると,少々難解に感じるかも知れません。 もし,そのように感じてしまった方は,是非サンプルコードを実際に動作させてみてください。 思ったよりも簡単に理解できると思います。

ラムダ式(Lambda expressions)

C#3.0

okButton.Click += (sender, e) => this.Method();

C#2.0

okButton.Click += delegate(object sender, EventArgs e) {
    this.Method();
};

上記はokButtonオブジェクトのClickイベントハンドラを,C#3.0ではラムダ式で,C#2.0では匿名メソッドで表現したものです。 このように,匿名メソッドで書くことができれば,ラムダ式で書くこともできます。

上記の2つコードを見比べると,ラムダ式の引数(sender, e)には型が明記されていないことに気が付くでしょう。 つまり,ここでも型推論が行われていることになります。

上記はイベントハンドラにラムダ式を用いた例で比較をしましたが,ラムダ式を実際に理解するには,デリゲートから順に理解していく方法がよいと思います。 以下に,例をあげてご説明します。

サンプルコード

次のコードは,加算・減算・乗算・除算用の4つのメソッドです。

それぞれが,あるコレクションに格納されている全オブジェクトのValue1,Value2のプロパティの値を計算し,その結果をResultプロパティに格納した後に,コンソールに出力しています。

private void Plus() {
foreach(var x in this.Collection) {
    x.Result = x.Value1 + x.Value2; // 足し算
    Console.WriteLine(x.Result);
}

private void Subtract() {
foreach(var x in this.Collection) {
    x.Result = x.Value1 - x.Value2; // 引き算
        Console.WriteLine(x.Result);
    }
}

private void Multiply() {
    foreach(var x in this.Collection) {
        x.Result = x.Value1 * x.Value2; // 掛け算
        Console.WriteLine(x.Result);
    }
}

private void Divide() {
    foreach(var x in this.Collection) {
        x.Result = x.Value1 / x.Value2; // 割り算
        Console.WriteLine(x.Result);
    }
}

4つのメソッドは,計算方法が異なることを除いてまったく同じ処理です。 従って,計算式のみ差し替えることができれば共通のロジックが使えます。

これを実現する方法として,デリゲート・匿名メソッド・ラムダ式が便利です。

デリゲートによる処理

以下のコードは,計算処理の部分をほかのメソッドに委譲することでロジックを共通しています。

なお,減算・乗算・除算メソッドも同様に記述できるため,以下のサンプルコードからは加算メソッドの例のみを示しています。

private void Plus() {
    this.Calculate(this.CalcAdd); // 計算を委譲するメソッド
}

// 共通計算メソッド
private void Calculate(Func<int, int, int> func) {
    foreach(var x in this.Collection) {
        x.Result = func(x.Value1, x.Value2); // 計算を委譲
        Console.WriteLine(x.Result);
    }
}

// 委譲されたメソッド
private int CalcAdd(int a, int b) {
    return a + b; // 足し算
}

Plusメソッドでは,共通計算メソッドに対して委譲するメソッドを引数として渡します。

共通計算メソッドのFunc<int, int, int>デリゲート型のfuncが,委譲するメソッドを受け取ります。 これによって,計算処理部分はfuncを呼び出し,その結果を受け取ることで共通化が実現します。

委譲するメソッドは,引数や戻り値がFunc<int, int, int>デリゲートと同じ数や型でなければいけません。

Func<>は,.NET Frameworkで定義されているジェネリックを利用したデリゲートです。 委譲されるメソッドはすべて以下のようになります。

private int Calc(int a, int b) {}

つまりFunc<>には,左からvalue1の型,value2の型,戻り値の型を表すことになるため,intを3つ指定することになります。

なお,戻り値を使わない場合はAction<>デリゲートを,さらに引数さえも使わない場合はActionデリゲートを使います。

匿名メソッドを用いた処理

次に,委譲すべきメソッド名を匿名にしてみます。 なお,共通計算(Calculate)メソッドは,上記と同じなので省略します。

public void Plus() {
    this.Calculate(
        delegate(int a, int b) { // 匿名メソッド
            return a + b;
        }
    );
}

Calculateメソッドの引数に,委譲すべきメソッド名を記述する代わりに,メソッドの処理を記述しています。 従って,先ほどのサンプルコードのように,委譲先となるCalcAddメソッドを別途用意する必要はありません。

ここでは,value1,value2引数の型は明記されていますが,戻り値の型がintであることは,Calculateメソッドのデリゲートに指定された型から推論されています。

著者プロフィール

伊藤達也(いとうたつや)

Microsoft MVP for Visual C#(April 2007 - March 2009)

自他共に認めるC#信者です。

(株)井沢電器設備(ビットラン)にて,主に業務管理システムの設計・開発を行っています。開発に関するご依頼ご相談など常時受け付けております。お気軽にご連絡ください。

Mailito@bitlan.net

コメント

  • re:ラムダ式

    ご指摘ありがとうございます。
    修正依頼をしておきますね。

    ご推察の通り、英語は苦手です。
    しかし L であることは知っていた筈なのですが、うーむ、何故間違ってしまったのだろう?

    Commented : #2  伊藤達也 (2008/06/12, 15:49)

  • ラムダ式

    ラムダ式は、英語では、Rambda expressionsではなく、Lambda expressionsなのでは? 英語は苦手?

    Commented : #1  あーっ (2008/05/27, 17:20)

コメントの記入