C# デリゲートについて

# tl;dr(まとめ）

C＃のデリゲートは、 **引数として関数を１つ受けとるデータ型(参照型)** の一種です。

C#には他にもnewキーワードで生成するデータ型としてクラス、構造体があります。

C#のデリゲートは、`+`, `-` 演算子を持ち、一つのデリゲート変数に複数のデリゲートを登録することが出来、 **マルチキャスト** と呼ばれる機能を持ちます。
（これが、C#のデリゲートの特筆すべき機能だと思います。）

# 書いたこと

1. デリゲートの宣言
2. デリゲートの生成と実行
    3. newキーワドによる生成
    4. 匿名メソッドによる生成
    5. ラムダ式による生成
3. デリゲートの+-演算 (マルチキャスト)

# デリゲートの宣言

```c#:デリゲートの宣言
delegate 戻り値の型 デリゲート名 (関数が引き受ける仮引数);
```

例: 「２つのint型の引数を受け取りint型の値を返す関数」のデリゲート宣言

```c#:Calculateデリゲートの宣言
// Calculateデリゲートの宣言
delegate int Calculate (int a, int b);
```

* 仮引数名が要ることが残念ですね。

# デリゲートの生成と実行

## newキーワードによる生成

```c#:newキーワードによるデリゲートの生成
using System;

// デリゲートの宣言
delegate void Printer (string s);

class Test {
    public static void Main() {
        // デリゲートの生成
        Printer print = new Printer(Console.WriteLine);
        // デリゲートの実行
        print("Hello World");
    }
}
```

* 引数としてクラスメソッド、もしくはインスタンスメソッドを受け取る

## 匿名メソッドによる生成

```c#:匿名メソッドによるデリゲートの生成
using System;

// デリゲートの宣言
delegate void Printer (string s);

class Test {
    public static void Main() {
        // デリゲートの生成
        Printer print = delegate (string s) {
            Console.WriteLine(s);
        };
        // デリゲートの実行
        print("Hello World");

        // 匿名メソッドはデリゲート変数に代入することも可能
        print = delegate (string s) {
            Console.WriteLine($"You inputed \"{s}\"");
        };
        print("I'm fine");
    }
}
/* 実行結果
Hello World
You inputed "I'm fine"
*/
```

* delegateキーワードを使うことで匿名メソッドを定義することが出来る
* 匿名メソッドはデリゲートの初期化もしくはデリゲート変数に代入する事ができる。
* つまり匿名メソッドはデリゲートを生成する

## ラムダ式による生成

```C#:ラムダ式によるデリゲートの生成
using System;

// デリゲートの宣言
delegate void Printer (string s);

class Test {
    public static void Main() {
        Printer print = (string s) => {
            Console.WriteLine($"You inputed \"{s}\"");
        };
        print("I'm fine");
    }
}
```

* ラムダ式も匿名メソッドと同じ様に、デリゲート変数の初期化、もしくは代入を行うことが出来る。
* つまりラムダ式もデリゲートを生成する

# デリゲートの+-演算 (マルチキャスト)

C#のデリゲートは一つのデリゲート変数に複数のデリゲートを登録することが出来ます。
（ **マルチキャスト機能** と呼ばれます。）

```C#:マルチキャストによる複数のデリゲートの登録
using System;

// デリゲートの宣言
delegate void Calculator (int other);

class Test {
    public static void Main() {
        var i = new Integer(5);
        // 一つのデリゲート変数に複数のデリゲートを登録できる
        var calculator = new Calculator(i.Add) + new Calculator(i.Sub) + new Calculator(i.Mul);
        // デリゲートを実行
        calculator(5);

        Console.WriteLine("-- 引き算を削除 --");

        // 登録したデリゲートを削除することが出来る 
        calculator -= new Calculator(i.Sub);
        // デリゲートを実行
        calculator(10);
    }
}

class Integer {
    public int val;
    public Integer(int n) {
        val = n;
    }
    public void Add(int n) {
        Console.WriteLine($"足し算の結果は: {val + n}です。");
    }
    public void Sub(int n) {
        Console.WriteLine($"引き算の結果は: {val - n}です。");
    }
    public void Mul(int n) {
        Console.WriteLine($"掛け算の結果は: {val * n}です。");
    }
}
/* 実行結果
足し算の結果は: 10です。
引き算の結果は: 0です。
掛け算の結果は: 25です。
-- 引き算を削除 --
足し算の結果は: 15です。
掛け算の結果は: 50です。
*/
```

* `+` 演算子を使って追加登録
* `-` 演算子を使って登録から削除

# Appendix

## 匿名メソッド

```c#:匿名メソッドの定義

delegate (仮引数) {
    // 式
}
```

* 生成するデリゲートの型は、コンパイラが推論してくれる

## ラムダ式

```c#:ラムダ式
(仮引数) => {
    // 式
}
```

* 生成するデリゲートの型は、コンパイラが推論してくれる

# 参照

[猫でもわかるC#プログラミング 第３版](https://www.amazon.co.jp/%E7%8C%AB%E3%81%A7%E3%82%82%E3%82%8F%E3%81%8B%E3%82%8BC-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E7%AC%AC3%E7%89%88-%E7%8C%AB%E3%81%A7%E3%82%82%E3%82%8F%E3%81%8B%E3%82%8B%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E7%B2%82%E4%BA%95/dp/4797385634)

[参照型 (C# リファレンス)](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/reference-types)