関数を返す関数。

この記事は [C# Advent Calendar 2019](https://qiita.com/advent-calendar/2019/c-sharp) の11日目です。

C#で「関数を返す関数」を作成するには、ActionやFuncなどのデリゲートを返す関数を作成します。「関数を返す関数」を作成することにより、一部の処理をシンプルに記述することができます。どのように使うのでしょうか。例を見てみましょう。

# Actionを返す関数
1秒間隔でコンソールに現在時刻を表示するプログラムを作成します。まずは通常？の書き方です。

```C#
using System.Timers;

class Foo
{
    private Timer MyTimer;
    public void TimerStart()
    {
        MyTimer = new Timer(1000);
        MyTimer.Elapsed += (s, e) => Console.WriteLine(DateTime.Now.ToString());
        MyTimer.Start();
    }

    public void TimerStop()
    {
        MyTimer.Stop();
    }
}
```

TimerStart()で開始し、TimerStop()で停止します。呼び出すのは以下のようになります。

```C#
var foo = new Foo();
foo.TimerStart();
Console.ReadKey();
foo.TimerStop();
```

これを「Actionを返す関数」で書きかえてみましょう。TimerStart()は、myTimerを停止させるためのActionを返す、という考え方です。

```C#
using System.Timers;

class Foo
{
    public Action TimerStart()
    {
        var myTimer = new Timer(1000);
        myTimer.Elapsed += (s, e) => Console.WriteLine(DateTime.Now.ToString());
        myTimer.Start();
        // myTimerを停止するActionを返す。
        return () => myTimer.Stop();
    }
}
```

はじめの方法からの変更点としては

- タイマーを停止するTimerStop()がなくなった
- MyTimerがローカル変数になった。
- TimerStart()しない限り、TimerStop()を呼び出せなくなった。

と、少しシンプルに記述することができるようになりました。その代り、呼び出し側に少し注意が必要です。これを呼び出す側は以下のように変化します。

```C#
var foo = new Foo();
var stop = foo.TimerStart();
Console.ReadKey();
stop();
```

myTimerはローカル変数だから、TimerStart()の終了後は利用できないのでは？と思われるかもしれません。しかし問題はありません。詳しい説明は省きますが、myTimerは戻り値のAction内で利用されているため、TimerStart()が終了しても開放されないのです。  
この方法は初めの方法に比べパフォーマンスが若干劣ります。しかしゲームなどのパフォーマンスが追及される場合でない限り、問題ないと考えます。

# Func<Action>を返す関数
先ほどの例は、関数が関数を返しました。これを進めると、関数の数珠つなぎにすることができます。別の例を示します。テキストファイルでログを作成するプログラムです。

```C#
public Func<string, Action> LogStart(string path)
{
    var writer = new System.IO.StreamWriter(path);
    return text => // Funcを返す
    {
        writer.WriteLine(text);
        return () => writer.Dispose(); // Actionを返す
    };
}
```

LogStart()はかなり複雑そうに見えますが、大丈夫です。よく見ると、LogStart()は「ある関数」を返し、ある関数はまた「別の関数」を返しています。詳しく見てみましょう。

LogStart()は、ログファイルを新規作成し、「ログに文字を書き込む関数」を返します。「ログに文字を書き込む関数」はさらに、「ログをクローズする関数」を返します。

これを利用する方法は以下の通りです。

```C#
var log = LogStart(@"C:\Debug\test.log");
log("Hello");   // LogにHelloを書き込む。
log("World")(); // LogにWorldを書き込んで、閉じる。
```

log("World")()の部分、こんな書き方してもいいんでしょうか？いいんです。でも少しわかりづらいですね。さすがにここまでくると、LogStartはActionを二つ返したほうがわかりやすいでしょう。

```C#
class LogControl
{
    public Action<string> Write { get; set; }
    public Action Close { get; set; }
}

public LogControl LogStart(string path)
{
    var writer = new System.IO.StreamWriter(path);
    return new LogControl()
    {
        Write = text => writer.WriteLine(text),
        Close = () => writer.Dispose()
    };
}
```

```C#
var log = LogStart(@"C:\Debug\test.log");
log.Write("Hello");
log.Write("World");
log.Close();
```

またはタプルで返すという方法もあります。

```C#
public (Action<string> Write, Action Close) LogStart(string path)
{
    var writer = new System.IO.StreamWriter(path);         // ファイルを作成し、
    Action<string> write = text => writer.WriteLine(text); // テキストを出力して、
    Action close = () => writer.Dispose();                 // 閉じる。
    return (write, close);
}
```

この書き方は、コメントに書きましたが、一つの関数の中で処理が順番に記述できるためわかりやすい、と私は感じています。

# まとめ
以上、「関数を返す関数」の例を見てきました。「関数を返す関数」は、関数内で作成したオブジェクトを、その関数が終わってからも利用可能にします。単なる関数ではなく、内部に状態を持つ小さなクラスのようにふるまいます。通常の関数と違い呼び出し方に注意点が必要ですが、よろしければ利用を検討してみてください。