クロージャでデリゲート

プログラミングC# 第6版 5.7


これもデリゲートがらみですね。

クロージャ、と何やら新しい名称が出てきました。

匿名メソッド内で外部で定義された変数を使用することで、その変数が匿名メソッドが存在する間ずっと参照可能な状態のことを言うそうです。

言葉だとわかりにくいですが、実装を見ると単純な話だとわかりますね。

using System;

class Program {
    static void Main () {
        int num = 1;
        
        // ラムダ式内で外部変数であるnumを使用
        Func<int,int> dobj = i => i + num;
        
        Console.WriteLine( dobj(1) );
    }
}
$ main
2

さて、これだけ見るといまいち、え?これがクロージャ?ってなりますよね。

しかしこのラムダ式内で使われているnumは外部のnumと繋がっているので、以下のような処理が可能となります。

using System;


class Program {
    static void Main () {
        int num = 1;
        
        // ラムダ式内で外部変数であるnumを使用
        Func<int,int> dobj = i => i + num;
        
        Console.WriteLine( dobj(1) );
        
        // ここでnumを書き換えてもう一度呼んでみると・・・
        num = 5;
        Console.WriteLine( dobj(1) );
    }
}
$ main
2
6

6と表示されるわけです!これは凄いですね。

このクロージャを応用すると以下のような処理もかけます。

using System;

class Program {
    static Func<int> Counter (int i) {
        return () => i++;
    }
    
    static void Main () {
        Func<int> cnt1 = Counter(1);
        Func<int> cnt2 = Counter(10);
        
        // 1からインクリメントするカウンタ
        Console.WriteLine( cnt1() );
        Console.WriteLine( cnt1() );
        Console.WriteLine( cnt1() );
        
        // 10からインクリメントするカウンタ
        Console.WriteLine( cnt2() );
        Console.WriteLine( cnt2() );
        Console.WriteLine( cnt2() );
    }
}
$ main
1
2
3
10
11
12

面白いですねぇ。この機能を使えば色々できそうです。

ちなみにFuncは引数を一つにすると引数なしの戻り値だけを指定できるみたいなので今回使ってみました。