デリゲート

デリゲート

 メソッドを参照するための型(デリゲート型)
 Delegateは委譲という意味で、委譲する内容は処理(メソッド)。
 定義方法は、処理を記述しない以外は、通常のメソッドと同様で、それにdelegateキーワードを指定する。
 デリゲート型の変数には、戻り値と引数リストが一致するメソッドを代入できる(派生クラス(ダウンキャスト)は可能)
delegate 戻り値 デリゲート名(引数リスト);

代入するメソッドについて

 デリゲート型の変数に代入するメソッドは、クラス(static)メソッド、インスタンスメソッドのどちらでも代入することができる。
 つまり、代入するメソッドの使用回数によらず、どこかに必ず1回は定義しなければならない。
 C#では、これを簡単にするために、以下の"匿名関数"と"ラムダ式"が存在する。

匿名関数

 デリゲート用の仕組みで、後述するラムダ式はさらに簡素化されて、機能も充実している。
delegate (引数リスト){処理}

ラムダ式

 関数を整数などの変数と同じように扱う。
 前述の匿名関数と異なりデリゲート以外の用途がある。
 一般的な定義は以下の①であるが、引数が明確な場合には②のように省略することができる。
(int n) => n > 0; // ①
n => n > 0; // ② 0と比較しているため、引数がint型であることは明確

マルチキャスト

 +,-演算子で複数のメソッドを代入することができる。
 複数のメソッドが代入されているデリゲートを呼び出すと、代入順に逐次実行される。
 戻り値が存在する場合は、最後の戻り値のみ取得できない。

述語

 デリゲートの主要な用途の1つで、条件を満たすかどうかを調べるメソッド(述語)を代入し、ほかのメソッドに渡す方法がある。

Action

 デリゲートの一種で、戻り値を持たないメソッド。
 定義は以下の通り。
public delegate void Action<in T> (T obj);

Func

 デリゲートの一種で、戻り値を持つメソッド。
 定義は以下の通り。
public delegate TResult Func<in T,out TResult> (T obj);

まとめ

 上記をまとめる。

delegate・Action

 文字列を2回表示する。
class Action101
{
  private delegate void DisplayDelegate(string str);
  
  // デリゲートを使用して2回文字列を表示する。
  internal static void DeisplayByDelegate(string[] strs)
  {
    var displayDelegate = new DisplayDelegate((string str) => Console.WriteLine("First Delegate: " + str));
    displayDelegate += (string str) => Console.WriteLine("Second Delegate: " + str);
    ExecDeisplayByDelegate(strs, displayDelegate);
  }
  
  // Actionを使用して2回文字列を表示する。
  internal static void DisplayByAction(string[] strs)
  {
    var displayAction = new Action<string>((string str) => Console.WriteLine("First Action: " + str));
    displayAction += (string str) => Console.WriteLine("Second Action: " + str);
    ExecDisplayByAction(strs, displayAction);
  }
  
  private static void ExecDeisplayByDelegate(string[] strs, DisplayDelegate displayDelegate)
  {
    foreach (var str in strs)
      displayDelegate(str); // 戻り値が存在しないのでOK
  }
  
  private static void ExecDisplayByAction(string[] strs, Action displayAction)
  {
    foreach (var str in strs)
      displayAction(str); // 戻り値が存在しないのでOK
  }
}

delegate・Func

5以上の偶数を表示する。
internal class Func101
{
  private delegate bool Condition(int n);
  
  internal static void DisplayGreaterThan5EvenNumberByDelegate(int[] nums)
  {
    var condition = new Condition(n => n >= 5);
    condition += n => n % 2 == 0;
    ExecDisplayGreaterThan5EvenNumberByDelegate(nums, condition);
  }

  internal static void DisplayGreaterThan5EvenNumberByFunc(int[] nums)
  {
    var condition = new Func<int, bool>(n => n >= 5);
    condition += n => n % 2 == 0;
    ExecDisplayGreaterThan5EvenNumberByFunc(nums, condition);
  }
  
  private static void ExecDisplayGreaterThan5EvenNumberByDelegate(int[] nums, Condition condition)
  {
    foreach (var num in nums)
    {
      var result = true;
      foreach (Condition cond in condition.GetInvocationList())
        result &= cond(num);
      if (!result) continue;
      Console.WriteLine("Delegate: " + num);
    }
  }

  private static void ExecDisplayGreaterThan5EvenNumberByFunc(int[] nums, Func<int, bool> condition)
  {
    foreach (var num in nums)
    {
      var result = true;
      foreach (Func<int, bool> cond in condition.GetInvocationList())
        result &= cond(num);
      if (!result) continue;
      Console.WriteLine("Delegate: " + num);
    }
  }
}