sheephumanのブログ

広汎性発達障害と診断されたASD孤立型。

アタシのC#勉強法(ソースコード移植法)2

前回の記事

sheephuman.hatenablog.com

 

 こんにちわ!美味しいわ! ひつじ!人間です。


CoCo壱のキノコカレー(大盛)

 

 今回も女の子口調で頑張るわ!モノホンのレイラちゃんじゃなくてもフリぐらいは出来るのよ!

ひつじさん(女性)だって同性(仲間)だと思わせちゃうわ!(本当です)

コードレビューを行う

 読むだけじゃなく、自分なりの理解を深めていく事が重要よ。
そこで、前回のコードをあたしなりに解釈してみたわ。

変数宣言

  private Restaurant _dataobject;
 private Stack<Restaurant> _undostack;
  private Stack<Restaurant> _redostack;

 
 Restaurant型の変数に、Stackクラスの変数宣言をしているわね。
型引数は同じね。Restaurantクラスは、主にDataGridViewに入力するためのパラメータを保持するクラスね。

Restaurant クラスの内部

 RestaurantChangedEventArgs型を引数として呼び出すデリゲートを宣言しているわ。

  public delegate void RestaurantChangedEventHandler(object sender, RestaurantChangedEventArgs e);


 public class RestaurantChangedEventArgs : EventArgs
    {
        private Restaurant _oldvalue;
        private string _description;

        RestaurantChangedEventArgs(Restaurant oldvalue, string description) { _oldvalue = oldvalue.DeepClone(); _description = description; }
    }

 EventArgsを継承したRestaurantChangedEventArgsクラスを定義することで、自作のイベントクラスを作ったわけね。
 
 ここで oldvalue.DeepClone() メソッドを呼んでいるわ。これもRestrauntクラス内に定義すべきメソッドという事よ。


   public Restaurant DeepClone()
        {
            Restaurant clone = new Restaurant();
            clone.NameSilent = this.Name;
            clone.CitySilent = this.City;
            clone.StateSilent = this.State;
            foreach (HealthScore score in Scores) clone.Scores.Add(score.DeepClone());
            return clone;
        }

 これらの変数は呼び出し側(UI側)で入力されて渡されているわ。これをStackすることでUndo Redoを実現しているわけよ。



UndoリストにStackされた入力値
f:id:sheephuman:20200702165810p:plain


処理の流れ

このとき、宣言されたList型の変数 "Score" を foreachでループ処理してるんだけど、呼び出し先のクラス 内でも同じDeepCloneメソッドを呼んでいるの。これは区別のために、HealthCloneメソッドと書き換えたわ。

 //HealthScore内のDeepCloneメソッド
   internal HealthScore HealthClone()
        {
            HealthScore clone = new HealthScore();
            clone.ValueSilent = Value;
            return clone;
        }



つまり、この時点で相当に入り組んだ設計になっているわ。
 RestaurantChangedEventを呼ぶ → DeepCloneメソッドを呼ぶ → List Score をforeachして、そのたびにHealthScoreメソッドを呼ぶ。
これがこのプログラムの基礎的な構造みたいね。

Delegeteとイベント

 各プロパティもset アクセサーでOnChangedメソッドを呼んでいるため、

public event RestaurantChangedEventHandler Changed;
   public List<HealthScore> Scores
        {
            get { return _scores; }
            set { OnChanged(new RestaurantChangedEventArgs(this, "Scores")); _scores = value; }
        }

 このように、プロパティに何かを代入(set)する度に自作イベントOnChanged を呼んでいるわ。
OnChangedメソッドはChangedイベントを呼んで → Delegete呼び出し → 前述のDeepClone()メソッド
と続くのね。
かなり込み入った仕掛けなのねコレ.....

何の為にこんな処理になっているかというと、UI側に入れる入力値が複数あるので、それに対応してStackするためらしいわね。






ここで重要なのが、 

  public event RestaurantChangedEventHandler Changed;

なの。このEvent型変数により、デリゲート RestaurantChangedEventHandler を呼び出しているみたいね。

csharp.keicode.com

通常は直接イベントハンドラを呼べてしまう事に問題があるみたいで、Delegete を介して呼び出したい。
そのためにEventキーワードで呼ぶのね(多分...)

感想

プロパティで代入するたびにイベントを発火させるって発想は面白いと思うわ。
DeepCloneのforeachループ内でHealthCloneを行うっていうのは処理的に疑問もないでもないけど....


特筆すべきなのは、RestrauntクラスとHealthScore内に保持したパラメータだけでこの複雑な処理を全部行っていることね。


この場合は

   foreach (HealthScore score in Scores) clone.Scores.Add(score.DeepClone());

となっているので、List型のScore変数にAddして、一時的にスタック情報として保持しているという事みたいね。

Form側(呼び出し側)では

        private Restaurant _dataobject;
        private Stack<Restaurant> _undostack;
        private Stack<Restaurant> _redostack;

呼び出し側(Form側)
         _undostack.Push(e.OldValue);
         _redostack.Clear();
         _dataobject = undostate.DeepClone();


などとしているので、データ入力する度にこれらが呼ばれて、前述の処理が行われているわ。
 

このサンプルプロジェクトはXMLファイルの保存と読み込み(ロード)なんかも出来るので、ちょっと参考にしたいわね。



少しは複雑なコードを理解出来るようになったかしら。
分からなかったら、とにかく図解やコードレビューなどを徹底すれば、だんだん分かるようになってくると思うわ。
ここまで読んでくれてありがとう。お礼にキスしてあげる。

f:id:sheephuman:20200702174358p:plain