前提 delegateとは
delegateとは関数の形(戻り値、引数)を予め定義してあとから中身を実装する仕組み。
using System;
public class Program {
// デリゲートの定義
public delegate void PrintDelegate(string message);
// デリゲートに関連付けるメソッド
public static void PrintToConsole(string message) {
Console.WriteLine(message);
}
public static void Main() {
// デリゲートのインスタンスを作成し、メソッドを関連付ける
PrintDelegate printDelegate = PrintToConsole;
// デリゲートを使用してメソッドを呼び出す
printDelegate("Hello, World!");
}
}
使いどころは、初期化処理や、終了処理など決まった箇所で決まった処理を実装するときに役に立つ。
using System;
public class Program {
// デリゲートの定義
public delegate void InitializationDelegate();
// 初期化処理メソッド
public static void InitializeDatabase() {
Console.WriteLine("Database initialized.");
}
public static void InitializeCache() {
Console.WriteLine("Cache initialized.");
}
public static void InitializeLogger() {
Console.WriteLine("Logger initialized.");
}
public static void Main() {
// デリゲートのインスタンスを作成し、初期化処理メソッドを関連付ける
InitializationDelegate initDelegate = InitializeDatabase;
initDelegate += InitializeCache;
initDelegate += InitializeLogger;
// デリゲートを使用してすべての初期化処理を呼び出す
Console.WriteLine("Starting initialization...");
initDelegate();
Console.WriteLine("Initialization complete.");
}
}
これにより初期化させたい処理が増減しても柔軟に対応ができる。
オーバーライドとdeligateの違い
オーバーライドは継承先の関数の処理を書き換える。
public class Animal {
public virtual void MakeSound() {
Console.WriteLine("Some sound");
}
}
public class Dog : Animal {
public override void MakeSound() {
Console.WriteLine("Woof!");
}
}
public class Program {
public static void Main() {
Animal myDog = new Dog();
myDog.MakeSound(); // "Woof!"が出力される
}
}
オーバーライドも、deligateとも広くみれば、呼び出し元のコードを変えずに処理を変える方法だが、
オーバーライドは継承先の処理を変えるもの
deligateは特定の処理を動的に呼び出すためのものである。
deligateでもポリモーフィズムを実現できなくはないが、素直にオーバーライドを使ったほうが認識のずれが起きにくい。
using System;
public class Program {
// デリゲートの定義
public delegate void AnimalSoundDelegate();
// デリゲートに関連付けるメソッド
public static void DogSound() {
Console.WriteLine("Woof!");
}
public static void CatSound() {
Console.WriteLine("Meow!");
}
public static void CowSound() {
Console.WriteLine("Moo!");
}
public static void Main() {
// デリゲートのインスタンスを作成し、メソッドを関連付ける
AnimalSoundDelegate soundDelegate;
// 犬の鳴き声を呼び出す
soundDelegate = DogSound;
soundDelegate();
// 猫の鳴き声を呼び出す
soundDelegate = CatSound;
soundDelegate();
// 牛の鳴き声を呼び出す
soundDelegate = CowSound;
soundDelegate();
}
}
まとめると
鳴き声の例のようにポリモーフィズムを実装したい場合、オーバライド使う。
イベントやコールバックを実装した場合deligateを使うと覚えても問題ない。
イベントとコールバック
どちらも特定のタイミングで関数を呼び出すための仕組みだが、求められているコンテキスト(場面)が違う。
イベントは特定の関数や状況が発生した時に呼ばれるもの。
言語的には += を使い処理を追加していける。
button.Click += (sender, e) => {
Console.WriteLine("Button clicked!");
};
コールバックは引数に関数をもち処理のを動的に変更することができる仕組みのこと
public void LoadData(Action<string> callback) {
// データの読み込み処理
string data = "Sample data";
callback(data);
}
LoadData((data) => {
Console.WriteLine("Data loaded: " + data);
});
C# におけるeventとActionの違い
前置きがながかった。delegateを利用したものがeventとActionになる。
eventは前述のとおり、特定の状況で実行したい処理を追加できるもの
例えばボタンがおされたら。
public class Button {
public event Action Clicked;
public void OnClick() {
if (Clicked != null) {
Clicked();
}
}
}
public class Program {
public static void Main() {
Button button = new Button();
button.Clicked += () => Console.WriteLine("Button was clicked!");
button.OnClick(); // "Button was clicked!"が出力される
}
}
Action は戻り値を持たないあらかじめ定義されているdelegateです。
public class Program {
public static void Main() {
Action<int> printNumber = (number) => Console.WriteLine("Number: " + number);
printNumber(5); // "Number: 5"が出力される
}
}
つまりActionの仕組みを利用したのがeventであるともいえる。
またeventを使う理由はeventを直接呼び出すことが禁止されているからです。
public class Button {
public event Action Clicked;
public void OnClick() {
if (Clicked != null) {
Clicked();
}
}
}
public class Program {
public static void Main() {
Button button = new Button();
button.Clicked += () => Console.WriteLine("Button was clicked!");
// button.Clicked(); // これはコンパイルエラーになります
button.OnClick(); // "Button was clicked!"が出力される
}
}
eventを使い直接呼び出せないようにすることでロバスト性が上がります。