4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【C#】デリゲート変数の中身を変更することで、特定のタイミングでメソッド内の一部の処理を変更する方法

Posted at

概要

タイトルがすごく悩みました。
デリゲートの使い方について、こういう使い方もあるという自分用のメモとして残します。

コード

SampleClassというクラスの中のフィールドに、WriteTextというデリゲートが定義されているとします。またコンストラクタでこのデリゲートに処理を入れています。(処理A)
このデリゲートはSampleClassDoSomething()というメソッド内で呼ばれているとします。

    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ファイルから入力値の復元や、デリゲートのWriteTextWriteToCsvFile()メソッドという画面の入力値を外部のCSVファイルに書き出す処理を設定しているとします。
また、画面Aを閉じたタイミングではSampleClassClose()メソッドが走るとします。

上記の場合、例えば以下のようにコンストラクタで画面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
        }
    }

終わりに

デリゲートの使用方法は最低限は把握していたつもりですが、少し発想を工夫すると上記のようなこともできるという個人的に新しい発見があったので良かったです。

4
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?