C# デリゲート(Delegate)

C# では、デリゲート(Delegate)は型安全な関数ポインタであり、メソッドをパラメータとして他のメソッドに渡すことを可能にします。

C# のデリゲート(Delegate)は、C または C++ の関数ポインタに似ています。デリゲート(Delegate)は特定のメソッドへの参照を保持する参照型変数であり、参照は実行時に変更できます。

デリゲートは C# で非常に一般的であり、イベント処理、コールバック関数、LINQ などの操作に使用されます。

すべてのデリゲート(Delegate)はSystem.Delegateクラスから派生します。

デリゲート(Delegate)の宣言

デリゲートは参照型であり、メソッドシグネチャを定義し、そのシグネチャに一致するメソッドを格納するために使用できます。デリゲートを使用すると、他のクラスのメソッドを呼び出すことができます。

デリゲートの宣言は、そのデリゲートが参照できるメソッドを決定します。デリゲートは、同じシグネチャを持つメソッドを指すことができます。

デリゲートを宣言する構文は次のとおりです:

public delegate <return type> <delegate-name> <parameter list>

中国語形式の説明:

public delegate 返回类型 委托名(参数类型 参数名, ...);

例えば、次のコードでは、2つの整数を受け取り、整数を返すデリゲートを定義します:

public delegate int MathOperation(int x, int y);

次の例のデリゲートは、単一のstringパラメータを持つメソッドを参照し、int型の変数を返すために使用できます。

public delegate int MyDelegate (string s);

デリゲート(Delegate)のインスタンス化

デリゲート型を宣言したら、デリゲートオブジェクトはnewキーワードを使用して作成する必要があり、特定のメソッドに関連付けられます。デリゲートを作成するとき、newステートメントに渡すパラメータはメソッド呼び出しのように記述しますが、パラメータは含めません。例:

public delegate void printString(string s);
...
printString ps1 = new printString(WriteToScreen);
printString ps2 = new printString(WriteToFile);

次の例では、デリゲートの宣言、インスタンス化、および使用法を示します。このデリゲートは、整数型のパラメータを持つメソッドを参照し、整数値を返すために使用できます。

例

using System;

delegate int NumberChanger(int n);
namespace DelegateAppl
{
   class TestDelegate
   {
      static int num = 10;
      public static int AddNum(int p)
      {
         num += p;
         return num;
      }

      public static int MultNum(int q)
      {
         num *= q;
         return num;
      }
      public static int getNum()
      {
         return num;
      }

      static void Main(string[] args)
      {
         // デリゲートインスタンスを作成
         NumberChanger nc1 = new NumberChanger(AddNum);
         NumberChanger nc2 = new NumberChanger(MultNum);
         // デリゲートオブジェクトを使用してメソッドを呼び出す
         nc1(25);
         Console.WriteLine("Value of Num: {0}", getNum());
         nc2(5);
         Console.WriteLine("Value of Num: {0}", getNum());
         Console.ReadKey();
      }
   }
}

上記のコードがコンパイルおよび実行されると、次の結果が生成されます:

Value of Num: 35
Value of Num: 175

デリゲートのマルチキャスト(Multicasting of a Delegate)

デリゲートオブジェクトは+演算子を使用して結合できます。

結合されたデリゲートは、結合された2つのデリゲートを呼び出します。同じ型のデリゲートのみを結合できます。

-演算子を使用して、結合されたデリゲートからコンポーネントデリゲートを削除できます。

デリゲートのこの便利な機能を使用すると、デリゲートが呼び出されたときに呼び出されるメソッドの呼び出しリストを作成できます。これはデリゲートのマルチキャスト(multicasting)とも呼ばれます。

次のプログラムは、デリゲートのマルチキャストを示しています:

例

using System;

delegate int NumberChanger(int n);
namespace DelegateAppl
{
   class TestDelegate
   {
      static int num = 10;
      public static int AddNum(int p)
      {
         num += p;
         return num;
      }

      public static int MultNum(int q)
      {
         num *= q;
         return num;
      }
      public static int getNum()
      {
         return num;
      }

      static void Main(string[] args)
      {
         // デリゲートインスタンスを作成
         NumberChanger nc;
         NumberChanger nc1 = new NumberChanger(AddNum);
         NumberChanger nc2 = new NumberChanger(MultNum);
         nc = nc1;
         nc += nc2;
         // マルチキャストを呼び出す
         nc(5);
         Console.WriteLine("Value of Num: {0}", getNum());
         Console.ReadKey();
      }
   }
}

上記のコードがコンパイルおよび実行されると、次の結果が生成されます:

Value of Num: 75

デリゲート(Delegate)の用途

次の例では、デリゲートの使用方法を示します。デリゲートはprintString入力として文字列を受け取り、何も返さないメソッドを参照するために使用できます。

このデリゲートを使用して2つのメソッドを呼び出します。1つ目は文字列をコンソールに出力し、2つ目は文字列をファイルに出力します:

例

using System;
using System.IO;

namespace DelegateAppl
{
   class PrintString
   {
      static FileStream fs;
      static StreamWriter sw;
      // デリゲートの宣言
      public delegate void printString(string s);

      // このメソッドはコンソールに出力します
      public static void WriteToScreen(string str)
      {
         Console.WriteLine("The String is: {0}", str);
      }
      // このメソッドはファイルに出力します
      public static void WriteToFile(string s)
      {
         fs = new FileStream("c:\\message.txt", FileMode.Append, FileAccess.Write);
         sw = new StreamWriter(fs);
         sw.WriteLine(s);
         sw.Flush();
         sw.Close();
         fs.Close();
      }
      // このメソッドはデリゲートをパラメータとして受け取り、それを使用してメソッドを呼び出します
      public static void sendString(printString ps)
      {
         ps("Hello World");
      }
      static void Main(string[] args)
      {
         printString ps1 = new printString(WriteToScreen);
         printString ps2 = new printString(WriteToFile);
         sendString(ps1);
         sendString(ps2);
         Console.ReadKey();
      }
   }
}

上記のコードがコンパイルおよび実行されると、次の結果が生成されます:

The String is: Hello World

デリゲートの削除

特定のメソッドが不要になった場合は、-=演算子を使用して、そのメソッドをデリゲートチェーンから削除できます。

例

public class Program
{
    public delegate void PrintMessage(string message);

    public static void PrintUpperCase(string message)
    {
        Console.WriteLine(message.ToUpper());
    }

    public static void PrintLowerCase(string message)
    {
        Console.WriteLine(message.ToLower());
    }

    public static void Main()
    {
        PrintMessage print = PrintUpperCase;
        print += PrintLowerCase;

        // PrintLowerCase メソッドを削除
        print -= PrintLowerCase;

        // デリゲートを呼び出す(PrintUpperCase のみが呼び出されます)
        print("Hello, C#");  // 出力: HELLO, C#
    }
}

デリゲートとイベント

デリゲートはしばしばイベント(Event)と一緒に使用されます。イベントは特殊なタイプのデリゲートであり、パブリッシュ/サブスクライブメカニズムに使用されます。

C# では、イベントは本質的にデリゲートをカプセル化した型であり、プログラム内の特定の操作に応答するために使用されます。

例

public class Button
{
    // イベントを定義
    public event EventHandler Click;

    // イベントを発生させるメソッド
    public void OnClick()
    {
        if (Click != null)
        {
            Click(this, EventArgs.Empty);  // イベントを呼び出す
        }
    }
}

public class Program
{
    public static void Main()
    {
        Button button = new Button();
       
        // イベントをサブスクライブ
        button.Click += Button_Click;

        // イベントを発生させる
        button.OnClick();  // "Button clicked!" を出力
    }

    private static void Button_Click(object sender, EventArgs e)
    {
        Console.WriteLine("Button clicked!");
    }
}

デリゲートの種類

C# では、いくつかの一般的なデリゲート型が提供されています:

1、Action

Action:値を返さないメソッドを表します。最大16個のパラメータを受け取ることができます。

Action<string> printMessage = Console.WriteLine;
printMessage("Hello");

2、Func

Func:戻り値を持つメソッドを表します。最大16個のパラメータを受け取り、最初のパラメータは入力パラメータ、最後のパラメータは戻り値の型です。

Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(3, 4));  // 输出 7

3、Predicate

Predicate:bool 値を返すメソッドを表し、通常は条件判断に使用されます。

Predicate<int> isEven = x => x % 2 == 0;
Console.WriteLine(isEven(4));  // 输出 True

デリゲートに関する注意事項

型安全性:デリゲートは型安全です。つまり、シグネチャが一致するメソッドのみをデリゲートに割り当てることができます。

匿名メソッドとラムダ式:匿名メソッドまたはラムダ式を使用してデリゲートインスタンスを作成し、コードを簡素化できます。

Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(5, 3));  // 输出 8

非同期呼び出し:デリゲートを BeginInvoke および EndInvoke メソッドと一緒に使用して、非同期呼び出しを行うことができます。

デリゲートは C# における非常に強力で柔軟な機能であり、イベント駆動型プログラミング、コールバック機構、関数型プログラミングスタイルの実装に役立ちます。コード再利用の能力を提供するだけでなく、プログラムのモジュール化の度合いも向上させます。デリゲートの使用を理解し習得することは、C# プログラミングにおいて非常に重要です。

その他の拡張