デリゲートの使い方

#●デリゲート
メソッドを参照するための型。
変数にメソッドを代入することができたり、メソッドの引数としてメソッドを渡すことができる。


```C#
[修飾子] delegate 戻り値の型 デリゲート名(引数の型 引数, ...)
```
修飾子として利用できるもの
・public(すべてのクラスからアクセス可能)
・internal(同じアセンブリ内からのみアクセス可能)


##●ソース

###戻り値なしの場合のデリゲート

```C#
class Program
{
    // string型の引数を受け取り、戻り値はvoidのデリゲート
    delegate void OutputProcess(string str);

    // 引数のデリゲートに対応したメソッド
    static void SayHello(string str)
    {
        Console.WriteLine(str);
    }

    // 引数でもらったデリゲートに対応したメソッドを実行
    void SayHelloWorld(string str, OutputProcess output)
    {
        output(str);
    }

    static void Main(string[] args)
    {
        // newキーワードでデリゲートを生成
        var outProcess1 = new OutputProcess(Console.WriteLine);

        outProcess1("Hello");

        // デリゲートに対応したメソッドを代入
        OutputProcess outProcess2 = SayHello;

        var p = new Program();
        // SayHelloWorldに引き渡し
        p.SayHelloWorld("Hello World", outProcess2);
    }
}
```

###戻り値ありの場合のデリゲート

```C#
// string型の引数を受け取り、戻り値はstringのデリゲート
delegate string Delegate(string str);

void Display(string[] data, Delegate output)
{
    foreach (var val in data)
    {
        Console.WriteLine(output(val));
    }
}

// 引数のデリゲートに対応したメソッド
static string DisplayExt(string str)
{
    return $"[{str}]";
}

static void Main(string[] args)
{

    var data = new[] { "あいうえお", "かきくけこ", "さしすせそ" };
    var p = new Program();

    // DisplayExtをデリゲートに代入
    Delegate outProcess = DisplayExt;
    // 引数としてoutProcessを渡す
    p.Display(data, outProcess);
}
```

###マルチキャスト
「+=」、「-=」演算子を用いることで、複数のメソッドを同時に実行することができる。


```C#

// 計算を実行するクラス
class Calculate
{
    int total = 0;

    public Calculate(int val)
    {
        total = val;
    }

    // デリゲートに対応したメソッド
    public void Add(int val)
    {
        Console.WriteLine($"{total} + {val} = {total += val}");
    }
    // デリゲートに対応したメソッド
    public void Sub(int val)
    {
        Console.WriteLine($"{total} - {val} = {total -= val}");
    }
}



// int型の引数を受け取り、戻り値はvoidのデリゲート
delegate void Delegate(int val);

static void Main(string[] args)
{
    var cal = new Calculate(10);

    // デリゲートに対応したメソッドを代入
    Delegate outProcess = cal.Add;

    // 足し算が1回実行される
    outProcess(10);

    // +=でSubメソッドを追加
    outProcess += cal.Sub;
    // 足し算が1回、引き算が1回実行される
    outProcess(5);

    Console.WriteLine("足し算を1つ削除");

    // -=でAddメソッドを1つ削除
    outProcess -= cal.Add;

    // 引き算が1回実行される
    outProcess(5);
    /* 実行結果
    10 + 10 = 20
    20 + 5 = 25
    25 - 5 = 20
    足し算を1つ削除
    20 - 5 = 15
    */

}
```

#●匿名メソッド
デリゲートに渡すことを目的としたメソッドを別に用意する必要がなくなる

匿名メソッドの構文

```C#
delegate(引数の型 引数, ...) {
 ... メソッドの本体...
}
```

##ソース

引数ありで例示したソースを匿名メソッドで表現する。

```C#
// 引数のFuncデリゲートに指定された処理を実行する
void Display(string[] data, Func<string, string> output)
{
    foreach (var val in data)
    {
        Console.WriteLine(output(val));
    }
}

static void Main(string[] args)
{
    var data = new[] { "あいうえお", "かきくけこ", "さしすせそ" };
    var p = new Program();

    // 匿名メソッド
    // delegateキーワードを利用、以降にメソッド本体
    p.Display(data, delegate (string str)
    {
        return $"[{str}]";
    });
}
```
よく使用されるデリゲートはライブラリで用意されている。(下記は一例)
Func`<string>`:引数なしでstring型を返す
Func`<string, string>`:引数はstring型でstring型を返す
Func`<int, string, bool>`:引数はint型、string型でbool型を返す

Action`<string>`:引数はstring型で戻り値はなし
Action`<int, double>`:引数はint型、double型で戻り値はなし


#ラムダ式
匿名メソッドで利用したdelegateキーワードを省略した形で表現することができる

##ソース

```C#
// 引数のFuncデリゲートに指定された処理を実行する
void Display(string[] data, Func<string, string> output)
{
    foreach (var val in data)
    {
        Console.WriteLine(output(val));
    }
}

static void Main(string[] args)
{
    var data = new[] { "あいうえお", "かきくけこ", "さしすせそ" };
    var p = new Program();

    // ラムダ式
    p.Display(data, (string str) =>
    {
        return $"[{str}]";
    });
    /*匿名メソッドとは違い、delegateキーワードを書かず「=>」でつなぐ
      p.Display(data, delegate (string str)
    */
}
```
***

#・参照
独習C#