デリゲート
・メソッドを参照するための型・C/C++でいうところの関数ポインタのような感じ
class Program
{
// デリゲートの宣言 シグネチャ(戻値と引数リスト)が指定できればOK
delegate void SampleDelegate(int id, string name);
static void Main(string[] args)
{
// 大昔はnew演算子を使っていた
SampleDelegate sd1 = new SampleDelegate(WriteToConsole);
sd1(1, "taro");
// 直接メソッドを指定できる
SampleDelegate sd2 = WriteToConsole;
sd2(2, "jiro");
// 今はラムダだよね
SampleDelegate sd3 = (id,name) => Console.WriteLine("ID:{0} , NAME:{1}", id, name);
sd3(3, "saburo");
// マルチキャストデリゲート
// 複数指定できる。追加した順に逐次実行
SampleDelegate sd4 = WriteToConsole;
sd4 += WriteToConsole_A;
sd4 += WriteToConsole_B;
sd3(4, "shiro");
// 非同期呼び出し
SampleDelegate sd5 = SomeHeavyProcedure;
// デリゲートを定義すると非同期メソッドも使えるようになる。
IAsyncResult asyncRet = sd5.BeginInvoke(5, "goro", null, null);
// メインスレッドでの重たい処理の代替
System.Threading.Thread.Sleep(1000);
// 非同期側のスレッドの終了待ち
sd4.EndInvoke(asyncRet);
}
static void WriteToConsole(int id, string name)
=> Console.WriteLine("ID:{0} , NAME:{1}", id, name);
static void WriteToConsole_A(int id, string name)
=> Console.WriteLine("Method_A # ID:{0} , NAME:{1}", id, name);
static void WriteToConsole_B(int id, string name)
=> Console.WriteLine("Method_B # ID:{0} , NAME:{1}", id, name);
static void SomeHeavyProcedure(int id, string name)
{
System.Threading.Thread.Sleep(500);
Console.WriteLine("SomeHeavyProcedure # ID:{0} , NAME:{1}", id, name);
}
汎用デリゲート
都度デリゲートを定義するのも煩雑なので、標準ライブラリに汎用デリゲート(定義済デリゲート)として提供されています
using System;
// 戻値がない場合は Action を使う。引数のリストを指定する
// 下記は delegate void HogeHoge(int x);と同等
Action<int> action = i => Console.WriteLine("{0} times", i);
// 戻値がある場合は Func を使う。引数リストの後に戻型を指定する
// 下記は delegate bool HogeHoge(int x , int y);の定義と同等
Func<int, int, bool> func = (x, y) => x > y;
イベント
デリゲートが自クラス内で使うものなのに対し、他クラスへのアクセッサとなるのがイベントです
class Tester
{
static void Main(string[] args)
{
var shop = new Shop{ Id = 1, Name = "Tokyo Shiten" };
shop.Name = "Kanagawa Shiten"; // この時点では何も起きない
// 名前変更時のイベントハンドラを登録
shop.OnNameChanged += name => Console.WriteLine("Name changed to [{0}]", name);
shop.Name = "Shizuoka Shiten"; // ここで変更した名前が出力される
shop.Id = 2; // ここではなにも起こらない
// ID変更時のイベントハンドラを登録
shop.OnIdChanged += id => Console.WriteLine("Id changed to [{0}]", id);
shop.Id = 3; // ハンドラが起動される
}
}
class Shop
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
// 値変更時にデリゲートを起動
OnNameChanged?.Invoke(_name);
}
}
// デリゲートの定義
public delegate void NameChangedHandler(string name);
// イベントの定義
public event NameChangedHandler OnNameChanged;
private int _id;
public int Id
{
get { return _id; }
set
{
_id = value;
// 値変更時にデリゲートを起動
OnIdChanged?.Invoke(_id);
}
}
// イベント定義 汎用デリゲートを使った例
public event Action<int> OnIdChanged;
}