概要
タイトルがすごく悩みました。
デリゲートの使い方について、こういう使い方もあるという自分用のメモとして残します。
コード
SampleClass
というクラスの中のフィールドに、WriteText
というデリゲートが定義されているとします。またコンストラクタでこのデリゲートに処理を入れています。(処理A)
このデリゲートはSampleClass
のDoSomething()
というメソッド内で呼ばれているとします。
public class SampleClass
{
private Action<string, string> WriteText { get; set; }
/// <summary>
/// コンストラクタ
/// </summary>
public SampleClass()
{
SetWriteText();
}
private void SetWriteText()
{
WriteText = (hoge, fuga) =>
{
// 処理A
};
}
private void DoSomething()
{
// 何かしらの処理
// デリゲート実行
WriteText?.Invoke("hoge", "fuga");
}
}
上記から、以下のSampleMethod()
メソッドのようにデリゲートの中身を一時的に書き換え、DoSomething()
を呼ぶ際にデリゲートであるWriteText
の処理だけ変更させることができます。
以下の場合、SampleMethod()
メソッドから呼ばれたDoSomething()
のWtiteText
は、処理Aではなく、処理Bが走ります。
また、backup
変数を保持しておき、DoSomething()
が走った後はこのbackup
変数を使用してWriteText
を元の状態に戻しているので、その後またWriteText
を呼び出すと処理Aが走るようになります。
public class SampleClass
{
private Action<string, string> WriteText { get; set; }
/// <summary>
/// コンストラクタ
/// </summary>
public SampleClass()
{
SetWriteText();
}
private void SetWriteText()
{
WriteText = (hoge, fuga) =>
{
// 処理A
};
}
private void SampleMethod()
{
var backup = WriteText;
WriteText = (hoge, fuga) =>
{
// 処理B
};
try
{
DoSomething();
}
finally
{
WriteText = backup;
}
}
private void DoSomething()
{
// 何かしらの処理
// デリゲート実行
WriteText?.Invoke("hoge", "fuga");
}
}
使用例
あまり使用するシーンが思いつきませんが、例えば以下のような場合?は使用することもできそうです。
例えばとある画面 (以下、画面Aと呼称します) があったとして、画面Aにはユーザーが入力する項目が沢山あるとします。画面Aを起動する前にはSampleClass
のコンストラクタが走り、外部のCSVファイルから入力値の復元や、デリゲートのWriteText
にWriteToCsvFile()
メソッドという画面の入力値を外部のCSVファイルに書き出す処理を設定しているとします。
また、画面Aを閉じたタイミングではSampleClass
のClose()
メソッドが走るとします。
上記の場合、例えば以下のようにコンストラクタで画面Aを開く前の入力値をDictionaryに保持しておき、画面Aが閉じられたタイミングで新しく入力値を保持するDictionaryを作成しておきます。そしてその2つの変数を比較して、差異があればデリゲートのWriteText
を介してWriteToCsvFile()
を呼出し、外部のCSVファイルに入力値を保存する、という形ができます。
※わざわざこういうことをしなくても他に方法はあると思いますが、使用できる例として挙げました。
public class SampleClass
{
private Dictionary<string, string> _initialDictionary = new Dictionary<string, string>();
private Action<string, string> WriteText { get; set; }
/// <summary>
/// コンストラクタ
/// </summary>
public SampleClass()
{
SetWriteText();
// csvファイルから入力値を復元
RestoreInputValue();
var backup = WriteText;
WriteText = (hoge, fuga) => WriteToDictonary(_initialDictionary, hoge, fuga);
try
{
DoSomething();
}
finally
{
WriteText = backup;
}
}
private void SetWriteText()
{
WriteText = (hoge, fuga) => WriteToCsvFile(hoge, fuga);
}
private void RestoreInputValue()
{
// 外部のcsvファイルから入力値を復元
}
private void Close()
{
var backup = WriteText;
var dictionary = new Dictionary<string, string>();
WriteText = (hoge, fuga) => WriteToDictonary(dictionary, hoge, fuga);
try
{
DoSomething();
}
finally
{
WriteText = backup;
}
bool isChanged = IsChanged(_initialDictionary, dictionary);
if (isChanged)
{
// 画面Aを開く前と閉じた後で変更があればcsvファイルに保存
WriteText?.Invoke("hoge", "fuga");
}
}
private void DoSomething()
{
// 何かしらの処理
// デリゲート実行
WriteText?.Invoke("hoge", "fuga");
}
private void WriteToCsvFile(string argA, string argB)
{
// 画面Aの入力値を外部のcsvファイルに書き込む処理
}
private void WriteToDictonary(Dictionary<string, string> dictonary, string argA, string argB)
{
// 画面Aの入力値を引数のDictionaryに書き込む処理
// 例えばKeyが項目名、Valueが入力値、等
}
private bool IsChanged(Dictionary<string, string> dictonary1, Dictionary<string, string> dictonary2)
{
// dictonary1とdictonary2を比較して差異があればtrueを返却、なければfalse
}
}
終わりに
デリゲートの使用方法は最低限は把握していたつもりですが、少し発想を工夫すると上記のようなこともできるという個人的に新しい発見があったので良かったです。