C#のtips

岩永信之さん(@ufcpp)のC#ページ

ufcpp.net

を見ながら勉強になったものを細々と書いておきます。

ガベージコレクション

ガベージコレクションはヒープ領域のオブジェクトを自動で開放してくれる機能。

C#ガベージコレクションは世代ガベージコレクションで、gen0,gen1,gen2という3つの世代で開放するタイミングが違う。

(開放されやすい順にgen0,gen1,gen2)

大きなオブジェクトはgen2で管理され、より長く残り続ける。

(統計的に、容量の大きいオブジェクトは長く利用されやすいという理由らしい)

ファイナライザー

オブジェクトが破棄される時に呼び出されるメソッドのことを、一般的にデストラクターという。

C#ではこれをファイナライザーとか呼んでいる。

class X
{
//~+クラス名でファイナライザーとして認識され、開放時に呼び出される
  ~X(){Console.WriteLine("finalizer called.");}
}
ガベージコレクションの流れ

ファイナライザーを持ったクラスオブジェクトを作る

ファイナライズリストに登録

どこからも参照されなくなると、F-reachableキューというガベージコレクション用の管理キューにそのオブジェクトを突っ込む

ファイナライズ専用スレッドでFinalizeメソッド(もしくは~Class()メソッド)を実行

完了したら、F-reachableからオブジェクトを取り出す

で、注意したいのがF-reachableキューの部分。

「どこからも参照されなくなったオブジェクト」が「F-reachableキューの中で参照される」。

つまり、まだ生きている状態。

still alive.

ので、ファイナライザでなんらかのstaticオブジェクトに自身を突っ込む処理を書いてしまうと、復活して永遠にヒープ領域から消えなくなる。

対処法

ファイナライザでどうとかすること自体がアンチパターンっぽい。

そもそも、ファイナライザは破棄処理を忘れた時の保険みたいなものなので。

解決策は

using(var obj = new Class())
{
//usingステートメントを抜けると、objは明示的に破棄される
}

と書いてしまい、ブロック抜ける時に必ずオブジェクトを破棄してやると良い。

C#のボックス化

ボックス化とはすごーくざっくり言うと値型を参照型に変換すること。

C#は値型も参照型もobjectオブジェクトを継承している。

Javaとかは、intやdoubleなどのプリミティブ型はオブジェクトとは違う、ということになっている。

ここで疑問になるのは、C#のintなどはobject型(参照型)を継承しているのに、値型ということ。

これはボックス化が自動で行われているので実現できている。

int i = 5;
//ボックス化。ヒープに領域を確保して値をコピー。
object obj = i;
//ボックス化解除。ヒープ領域の値をスタックにコピー。
int j = (int)obj;
で、これがどうかしたの?

ヒープ領域の確保はスタック領域の確保に比べてめっちゃ重いです。

ので、できるだけ避けましょう。

避けるってどうすんの

object型に値型を突っ込むの辞めるだけでokです。

varとかで受け取れば楽ですね。

もしくは、ジェネリックメソッド/ジェネリッククラスを作ることをお勧めします。