[Effective C#] 項目35 イベントハンドらよりもオーバーライドを優先すること
本当に日が空きました。一月から新しいお仕事です。難易度がむっちゃ高いので復習の意味も込めて Effective C# の紹介再開です。
本日の Effective C# は「項目35 イベントハンドらよりもオーバーライドを優先すること」です。
たとえば、Form が開かれた時の処理を、Form.Load イベントに登録するか、または Form.OnLoad() メソッドをオーバーライドするかの二種類の手段があります。
デザイナで作業しているとついついイベントで処理していますが、これはけた方がいいというお話です。理由として以下のものがあげられています。
- イベントハンドラはオーバーライドよりも重たい処理である。
- メソッドがイベントハンドラかどうかわかりにくい。
- イベントハンドラは呼び出される保証がない。
ちなみに、三番目のメソッドがイベントハンドラかどうかわかりにくいというのは、イベントハンドラの登録するコードとイベントハンドラが別々の個所になる C# 特有の問題です。もちろん、VB.NET も AddHandler ステートメントを使えば同様の問題が起きるのですが、Handles キーワード を使えばその問題はありません。Handles は数少ない VB.NET が C# に勝っている部分だと思います。
イベントハンドラは呼び出される保証がないってのは登録しているのになぜ? というのがあるんですが、以下のコードを見てください。
internal class Program { public EventHandler<EventArgs> Load; private static void Main(string[] args) { var program = new Program(); program.Load += Load1; program.Load += Load2; try { program.OnLoad(); } catch (Exception ex) { Debug.WriteLine(ex); } } public virtual void OnLoad() { EventHandler<EventArgs> h = Load; if (h != null) { h(this, new EventArgs()); } } private static void Load1(object sender, EventArgs e) { Debug.WriteLine("Run Load1()."); throw new InvalidOperationException(); } private static void Load2(object sender, EventArgs e) { Debug.WriteLine("Run Load2()."); } }
Load1() メソッドで例外がおきたせいで、Load2() は実行されません。例外起きたから当たり前じゃないか! と思うかもしれませんが、必ず実行してほしい処理というのがあった場合は非常に困ります。
また、イベントハンドラはこのように複数登録できますが、この処理は必ず最初に実行したいとか、逆に後始末なので必ず最後に実行したいとか、そういうのができません。オーバーライドならば、スーパークラスのメンバを先に呼び出すか後で呼び出すかコントロールできます。
とはいえ、書籍にも書いてありますが、イベントハンドラを絶対に使うなというわけではありません。外部クラスはイベントハンドラを利用せざるをありませんし。
経験上、イベントハンドラの多用は確かに痛い目見ます。イベントハンドラを実行順序に影響しやすいように書いた場合、下手に登録すると予想外の動きをします。本当はそういったものをイベントで実行してはいけないんですが、若気の至りでつい…。
コメントを追加