[C#] 23. デリゲート(delegate)


Study / C#    作成日付 : 2019/07/15 02:25:26   修正日付 : 2021/09/06 18:56:03

こんにちは。明月です。


この投稿はC#のデリゲート(delegate)を使う方法に関する説明です。


C#のデリゲートは代理子のいう意味でC++の関数ポインタと似ている概念を持っているキーワードです。つまり、関数ポインタとは関数式(function)をインスタンスのポインタみたいに認識して変数に値を格納するかパラメータに渡して実行する代理に実行する役をします。

using System;
 
namespace Example
{
  class Program
  {
    // デリゲート宣言
    delegate void PrintDelegate(String str);
    // 出力関数
    public void Print(String str)
    {
      // コンソール出力
      Console.WriteLine(str);
    }
 
    public Program() 
    {
      // デリゲートにメソッドを格納
      // デリゲートの変換タイプ、パラメータが一致するべき。
      PrintDelegate pd = new PrintDelegate(Print);
      // デリゲート実行
      pd("Hello World");
    }
    static void Main(string[] args)
    {
       // インスタンスを生成してコンストラクタを実行
       new Program();
       // 任意のキーを押してください
       Console.WriteLine("Press any key...");
       Console.ReadLine();
    }
  }
}


上の例でPrint関数をデリゲートで使ってポインタでインスタンスのポインタを変換してpdの変数をインスタンス値みたいに使えます。

実行はデリゲートの変数でただ関数を呼び出すみたいにパラメータ値を入れれば実行されて、コンソール出力することを確認できます。


上の式はデリゲートを説明するために凄くシンプルに作成したことで実際は変数の値みたいに使ういい点があります。

using System;
using System.Collections.Generic;

namespace Example
{
  // インタフェース
  interface INode
  {
    // 関数宣言
    void Print(String str);
  }
  // デリゲートがあるクラス
  class Example : INode
  {
    // 変換値がvoidで、Stringパラメータが一つあるデリゲート
    // デリゲートを外部から使うためにアクセス修飾子をpublicで設定するべき。
    public delegate void PrintDelegate(String str);
    // 関数ポインタを格納するためのリスト
    private List<PrintDelegate> list = new List<PrintDelegate>();
    // 関数追加
    public void AddDelegate(PrintDelegate func)
    {
      list.Add(func);
    }
    // Print関数を実行すればリストに格納された関数を実行する。
    public void Print(String str)
    {
      // 繰り返しでリストに格納する関数を取得。
      foreach (var item in list)
      {
        // 関数を実行
        item(str);
      }
    }
  }
  // Node1クラス
  class Node1 : INode
  {
    // Print関数
    public void Print(String str)
    {
      // コンソール出力
      Console.WriteLine("Node1 print - " + str);
    }
  }
  // Node2クラス
  class Node2 : INode
  {
    // Print関数
    public void Print(String str)
    {
      // コンソール出力
      Console.WriteLine("Node2 print - " + str);
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // デリゲートがあるクラスのインスタンスを生成
      Example example = new Example();
      // Node1クラスのインスタンスを生成
      INode node1 = new Node1();
      // Node2クラスのインスタンスを生成
      INode node2 = new Node2();
      
      // デリゲートがあるクラスにNode1クラスのPrint関数を登録
      example.AddDelegate(node1.Print);
      // デリゲートがあるクラスにNode2クラスのPrint関数を登録
      example.AddDelegate(node2.Print);
      
      // デリゲートがあるクラスのPrint関数にTest値を格納して実行
      example.Print("Test");

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例をみればExample클래스とNode1、Node2クラスをINodeインタフェースを継承しました。

上の形式はデザインパターンの中で合成パターンです。ExampleクラスにNode1クラスのPrint関数とNode2クラスのPrint関数を入れてExampleクラスのPrint関数を実行すれば同時に実行されるパターンです。


上の例は私がListにデリゲートを格納して関数ポインタを管理するようにしました。

でも、デリゲートは関数を管理するリスト機能もあります。+=と-=の対入演算子を通って追加、削除をできます。

using System;
using System.Collections.Generic;

namespace Example
{
  // インタフェース
  interface INode
  {
    // 関数宣言
    void Print(String str);
  }
  // デリゲートがあるクラス
  class Example : INode
  {
    // 変換値がvoidで、Stringパラメータが一つあるデリゲート
    // デリゲートを外部から使うためにアクセス修飾子をpublicで設定するべき。
    public delegate void PrintDelegate(String str);
    // 関数ポインタを格納するためのリスト
    private PrintDelegate list;
    // 関数追加
    public void AddDelegate(PrintDelegate func)
    {
      list += func;
    }
    // 関数削除
    public void RemoveDelegate(PrintDelegate func)
    {
      list -= func;
    }
    // Print関数を実行すればリストに格納された関数を実行する。
    public void Print(String str)
    {
      list(str);
    }
  }
  // Node1クラス
  class Node1 : INode
  {
    // Print関数
    public void Print(String str)
    {
      // コンソール出力
      Console.WriteLine("Node1 print - " + str);
    }
  }
  // Node2クラス
  class Node2 : INode
  {
    // Print関数
    public void Print(String str)
    {
      // コンソール出力
      Console.WriteLine("Node2 print - " + str);
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // デリゲートがあるクラスのインスタンスを生成
      Example example = new Example();
      // Node1クラスのインスタンスを生成
      INode node1 = new Node1();
      // Node2クラスのインスタンスを生成
      INode node2 = new Node2();

      // デリゲートがあるクラスにNode1クラスのPrint関数を登録
      example.AddDelegate(node1.Print);
      // デリゲートがあるクラスにNode2クラスのPrint関数を登録
      example.AddDelegate(node2.Print);
      // デリゲートがあるクラスのPrint関数にTest値を格納して実行
      example.Print("Test");

      // デリゲートでnode1.Print関数を削除
      example.RemoveDelegate(node1.Print);
      // デリゲートがあるクラスのPrint関数にTest値を格納して実行
      example.Print("Hello world");

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


上の例には二つ目のListクラスの変わりにデリゲートで関数式を追加、削除することができます。


最近にはC#コードでラムダ式(lambda)をよく見えますが、このラムダ式がデリゲート基盤で生成して使います。

using System;
using System.Collections.Generic;

namespace Example
{
  // デリゲートがあるクラス
  class Example
  {
    // 変換値がvoidで、Stringパラメータが一つあるデリゲート
    // デリゲートを外部から使うためにアクセス修飾子をpublicで設定するべき。
    public delegate void PrintDelegate(String str);
    // 関数ポインタを格納するためのリスト
    private PrintDelegate list;
    // 関数追加
    public void AddDelegate(PrintDelegate func)
    {
      list += func;
    }
    // 関数削除
    public void RemoveDelegate(PrintDelegate func)
    {
      list -= func;
    }
    // Print関数を実行すればリストに格納された関数を実行する。
    public void Print(String str)
    {
      list(str);
    }
  }
  class Program
  {
    // 実行関数
    static void Main(string[] args)
    {
      // デリゲートがあるクラスのインスタンスを生成
      Example example = new Example();
      // ラムダ式で関数を追加
      example.AddDelegate((str) =>
      {
        Console.WriteLine("Lambda - " + str);
      });
      // デリゲートがあるクラスのPrint関数にTest値を格納して実行
      example.Print("Test");

      // 任意のキーを押してください
      Console.WriteLine("Press any key...");
      Console.ReadLine();
    }
  }
}


ラムダ式は匿名関数の意味で関数で名前がない関数という意味です。このラムダ式は関数名が存在しないので関数ポインタだけ存在します。

つまり、ラムダ式を使うためにはこのデリゲートで関数のポインタを持つべきだと意味です。

ラムダ式はラムダ式を説明する時にもっと詳細に説明します。


ここまでC#のデリゲート(delegate)を使う方法に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

#C#
最新投稿