C#のデリゲートとラムダ式

# 概要

C#の`Delegate`は型です。メソッドを表す型です。
よく使われる`Func`は戻り値のある`Delegate`型、`Action`は戻り値が`void`の`Delegate`型です。

`Delegate`を使うためにいちいちメソッドを宣言するのが面倒なので作られたのが匿名関数です。
ラムダ式は匿名関数であり、~~デリゲートです。~~`Delegate`型のインスタンス化を行います。




# Delegate型とdelegate句

`Delegate`型を使うときには、どんな引数や戻り値を持つメソッドなのかを事前に定義する必要があります。

```C#
delegate int Calculate(int a, int b;)
```

`Calculate`という名前の`Delegate`型を（正確には継承したクラスを）定義しました。
`delegate`句はC#の文法で、`Delegate`を継承したクラスを生成します。なお`Delegate`を直接継承したクラスは作れません。

このままでは`int`2個を引数にとって何か計算して`int`を返すことしか分からない抽象的なものです。具体的にどんな計算をするか定義してあげましょう。

```C#
public void Execute()
{
    Calculate sum = getSum;
	Calculate gcd = GetGcd;
}

private static int GetSum(int a, int b)
{
    return a + b; 
}

private static int GetGcd(int a, int b)
{
	if (b == 0) return a;
    return GetGcd(b, a % b);
}
```

これで`Calculate`型の変数`sum`には和を計算するメソッド`GetSum`が、`gcd`には最大公約数を計算するメソッド`GetGcd`が定義されました。

```C#
Console.WriteLine(sum(3, 5));	// => 8
Console.WriteLine(gcd(85, 25));	// => 5
```

使うときはメソッドの呼び出しと一緒で引数を渡します。



# ActionとFunc

`Calculate`という名前に意味はありません。たとえ`int`でなく`long`で計算するとしても`Calculate`でしょう。そこで`delegate`を使った定義を省略する方法があります。

```C#
public void Execute()
{
	Func<int, int, int> sum = GetSum;
	Func<int, int, int> gcd = GetGcd;
    Action<int> writeInfo = WriteInfo;
    writeInfo(sum(3, 5));	// => 計算結果は8です
}

private static void WriteInfo(int num)
{
	Console.WriteLine("計算結果は" + num.ToString() + "です");
}
```

`Action`は戻り値が`void`の`Delegate`型、`Func`はそれ以外の`Delegate`型です。
`Func`はジェネリックの最後の要素が戻り値です。`Action`はすべて引数です。

やっていることは`delegate`で`Calculate`を定義して......と同じです。



`Action`や`Func`は型なのでメソッドの引数や戻り値にできます。

```C#
public void Execute()
{
	WriteInfo(GetSum, 3, 5); // => 計算結果は8です
}

private static void WriteInfo(Func<int, int, int> calculate, int a, int b)
{
	Console.WriteLine("計算結果は" + calculate(a, b).ToString() + "です");
}
```

`GetSum`メソッドを直接渡せました。やはりやっていることは今までと同じで記法が変わっただけです。



# 匿名メソッド式によるメソッド宣言の省略

`GetSum`メソッドは引数の和を返すだけのシンプルなものでした。
これだけのためにメソッド名を考えたりしたくはないですし、使う場面と宣言の場所が離れて処理を追いにくくなってしまいます。

そこでメソッドを使う場面で直接定義する記法があります。

```C#
public void Execute()
{
	Func<int, int, int> sumFunc = delegate (int a, int b) { return a + b; };
	WriteInfo(sumFunc, 3, 5);	// => 計算結果は8です
}
```

ついに1行になりました。
`(int a, int b)`が引数、`{}`内がメソッドの処理を表します。
これで`WriteInfo`に渡している部分がメソッド宣言と近くなり、何をしているのか分かりやすくなります。

ただし現在は後述するラムダ式を使うので、匿名メソッド式を書く機会はまずないです。



# ラムダ式

匿名メソッド式をさらに省略して書ける記法がラムダ式です。

```C#
public void Execute()
{
	Func<int, int, int> sumFunc = ((a, b) => a + b);
	WriteInfo(sumFunc, 3, 5);   // => 計算結果は8です
}
```

ラムダ式の特徴として、型推論によって引数の型や`return`を省略できます。ただしメソッド処理の内部が2行以上の場合は`{}`でくくって`return`を明示します。

```C#
Func<int, int, int> gcdFunc = null;		// 再帰のときは初期化しないとコンパイルエラー
gcdFunc = (a, b) =>
{
	if (b == 0) return a;
    return gcdFunc(b, a % b);
};
```

注意点として、ラムダ式を再帰関数として使う場合は`null`などで初期化する必要があります。でないと未割当の参照によるコンパイルエラーになります。

他にもイテレータ化ができないなどラムダ式にはいくつか制限があります。C#7でローカル関数が導入されたのはこの問題を解決するためだそうです。

また、ラムダ式はデリゲートだけでなく[式木（Expression Trees）](https://ufcpp.net/study/csharp/dynamic/sp3_expression/)としても扱えますが、本筋から逸脱するので言及はしません。

### 追記
ラムダ式は内部で必要に応じた`Delegate`型のインスタンスを生成しています。

```C#
gcdFunc = new Func<int, int, int>((a, b) =>
{
    if (b == 0) return a;
    return gcdFunc(b, a % b);
});
```
この`new`を省略できる記法だそうです。




# LINQ

ラムダ式が書けるとなにが嬉しいのかという話になります。
よくセットで上げられるLINQを考えてみます。

```C#
public void Execute()
{
	var items = new int[] { 1, 2, 3 };
	items.Select(Add10).ToArray();  // => [11, 12, 13]
}

private static int Add10(int a)
{
	return a + 10;
}
```

`Select`の引数は`Func`なので、メソッドを別で定義してやれば渡すことができます。
でも10じゃなく100を足したくなったらそのたびにメソッドを作るんでしょうか。

ラムダ式を使うとこう書けます。

```C#
public void Execute()
{
	var items = new int[] { 1, 2, 3 };
	items.Select(a => a + 10).ToArray();  // => [11, 12, 13]
}
```

LINQは`IEnumerable<T>`型に対して定義されたメソッド群で、戻り値も（基本的に）`IEnumerable<T>`型です。だからメソッドチェーンで書けます。
`IEnumerable<T>`は順番に要素にアクセスする方法が定義されただけの集合です。

LINQは引数にメソッドを取り、元の`IEnumerable<T>`の各要素をメソッドに渡す、というものです。
上の例だと`Sum10`の引数`a`に`items`の要素`1,2,3`が順番に渡されています。



# まとめ

ラムダ式は関数型の考えを取り入れたものだとかいわれることがありますが、C言語の時代にもあった関数ポインタと考え方は一緒です。新しいものでも怖いものでもないです。


## 参考

[デリゲート](https://ufcpp.net/study/csharp/functional/sp_delegate/)
[ローカル関数と匿名関数](https://ufcpp.net/study/csharp/functional/fun_localfunctions/)
[【LINQの前に】ラムダ式？デリゲート？Func<T, TResult>？な人へのまとめ【知ってほしい】](https://qiita.com/RyotaMurohoshi/items/740151bd772889cf07de)