2009年10月11日

【ラムダ式】 上位スコープ変数の参照

ラムダ式は、名前の無い即席メソッドというだけでなくもう一つ重要な性質があります。
なんと、本来スコープ外であるはずの上位スコープの変数にアクセス出来るのです。
まずは、以下のコードを御覧ください。

class Program
{
    static void Main(string[] args)
    {
        // Mainのローカル変数
        int count = 0;
        // intを返すラムダ式の定義
        Func<int> getCount = () =>
        {
            // Mainのローカル変数を操作している
            count++;
            // インクリメントした値を返す
            return count;
        };

        // 引数にラムダ式を渡して実行
        PrintCounter(getCount);
        // 最後にPrintCounterメソッド実行後のcount値を表示
        Console.WriteLine("ローカル変数countの値={0}", count);

        // <結果>
        // ラムダ式の戻り値=1
        // ラムダ式の戻り値=2
        // ラムダ式の戻り値=3
        // ローカル変数countの値=3
    }

    static void PrintCounter(Func<int> getCount)
    {
        // ラムダ式を3回実行して、その戻り値を表示
        Console.WriteLine("ラムダ式の戻り値={0}", getCount());
        Console.WriteLine("ラムダ式の戻り値={0}", getCount());
        Console.WriteLine("ラムダ式の戻り値={0}", getCount());
    }
}
getCountは、引数なし、戻り値int型のラムダ式として定義されています。
このラムダ式から、本来スコープ外でアクセス出来ないはずのローカル変数countを操作しています。(最後にcountの値を表示してみても、確かにローカル変数の値が変更されていることが分かります)
実際順に処理を追ってみると、明らかに今までの常識外の動作であることが分かります。
まず、PrintCounterメソッドにgetCountラムダ式を引数として渡します。
PrintCounterメソッドでは、getCountを3回呼び出しますが、このラムダ式の中でMainメソッドのローカル変数であるcountを操作しています。
countを引数で渡していないにも関わらず、上位スコープのローカル変数の値を変更しているのです。
これは、オブジェクト指向の考えを覆すほどの大きな変化です。
オブジェクト指向では、GOF本等で書かれている通り、流動的要素をカプセル化する為に想定する変更箇所の外部インターフェースを事前に定義します。
つまり、抽象クラスやインターフェース(abstract classとinterface)をあらかじめ定義して、そのインターフェースに合うように具象クラスを作っていきます。
しかし、ラムダ式を使うことによってあらかじめ定義された引数以外の値すらメソッド内で操作出来るようになるのです。
このことは、事前に定義された外部インターフェースに縛られること無く、柔軟な拡張が行える可能性を秘めています。
次回は、この性質を使った実用的な例を解説します。

ラムダ式の先頭記事へ ラムダ式の次の記事へ
タグ:ラムダ式
posted by 吾一 at 23:55| 2. ラムダ式 | このブログの読者になる | 更新情報をチェックする
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。