Effective C# 3rd 読書メモ 11 .NETのリソース管理を理解する

.NETにおいては、実行環境(CLR: Common Language Runtime)のメモリ管理とGCを理解することが重要である。

C++とC#の大きな違いの一つは、ファイナライザの挙動の違いである。
C++とは異なり、C#ではファイナライザがいつ実行されるかはわからない。
また、ファイナライザの使用にはパフォーマンス上の悪影響もある。

リソースの破棄を実行したい場合は、ファイナライザではなくIDisposableインターフェイスを使うべきである。

参考: Fundamentals of Garbage Collection

Effective C# 3rd 読書メモ 10 new修飾子はベースクラスが変更された場合にのみ使用する

new修飾子

new修飾子を使うと、ベースクラスから継承したvirtualでないメソッドを再定義できる。
new修飾子を使わなくても隠蔽はできるが、コンパイル時に警告が出る)
しかし、継承関係にあるクラスのもつ同名のメソッドがコンテキストによって異なる振る舞いをすると、クラスの利用者からは予測がしづらい。
new修飾子の使用は原則として避け、異なる振る舞いには異なる名前をつけるべきである。

また、new修飾子による再定義を避けるべき、というのは、ベースクラス定義時に何でもvirtualで宣言しろということではない。
ポリモーフィックな振る舞いをすべきプロパティ・メソッドにのみvirtualを使用し、それ以外はオーバーライドを許さないようにすべきである。

なお、new修飾子を使ってもよい場面が1つだけある。
それは、ベースクラスに、サブクラスで定義していたのと同じ名前のメソッドが追加された場合である。
この場合、原則としては自分の定義したメソッドの名前を変更すべきだが、全てを変更するコストが高くつきすぎる場合には、自分のメソッドにnew演算子を追加して、名前を変更せずにメソッドを呼び出せるようにすることができる。

参考:new 修飾子 (C# リファレンス)

Effective C# 3rd 読書メモ 09 ボックス化とボックス化解除を最小限にする

値型と参照型の間の暗黙的な型変換(ボックス化、ボックス化解除)にはパフォーマンス上の影響がある。
ボックス化とボックス化解除はできるだけ避けるべき。

たとえば、以下のコードではボックス化が発生する。

文字列を組み立てる段階で、intに対してToString()を実行するために、コンパイラがボックス化のための命令を生成する。
ボックス化を避けるには、以下のように明示的なメソッド呼び出しを行う。

このように、System.Objectへの暗黙的な型変換に注意することで、ボックス化を最小限にすることができる。

参考: ボックス化とボックス化解除 (C# プログラミング ガイド)

Effective C# 3rd 読書メモ 08 イベント呼び出しにはnull条件演算子を使用する

C# 6.0で導入されたnull条件演算子を使うと、イベント呼び出し時のnullチェックを簡潔に書くことができる。
以下のコードは、イベントハンドラの呼び出しを行う際の古典的なイディオムである。

このように、イベントハンドラの呼び出し時にはnullチェックが欠かせない。
さらに、スレッドセーフにするために、var handler = Updatedのように変数に代入を行う必要がある。
というのも、nullチェックの実行中に、別のスレッドによってUpdatedプロパティにnullが設定されている可能性があるためである。

null条件演算子(?.)を使うと、上記イディオムを以下のように書くことができる。

?.演算子は、演算子の左辺を評価し、nullでない値なら、右辺が実行される。
左辺がnullなら、右辺は実行されず、次の文へ進む。
このコードは、元のイディオムと同じようにスレッドセーフでもある。

Effective C# 3rd 読書メモ 07 コールバックはデリゲートによって実装する

メソッドの挙動を呼び出し側でカスタマイズしたい場合には、デリゲートを使用してコールバックを実装する。

LINQでデリゲートを使う例:

マルチキャストデリゲートについては、戻り値は最後に呼び出されたデリゲートの返した値になる点に注意が必要。
以下のようにすることでマルチキャストデリゲートの各デリゲートの戻り値を確認できる。