はじめに
WeakReference、使ってますか。私はあまり使っていません。その存在は知っていても、使う機会はあまりないという人は、私にかぎらず多くいるのではないでしょうか。
もちろん、WeakReference が何のためにあるかはご存知のことと思います。知らなかったという方は、岩永さんに感謝しながら、以下の記事を読んで勉強しておきましょう。
WeakReference と WeakReference<T>
さて、ここからが本題です。
WeakReference には、System.WeakRefenence と、System.WeakReference<T> がありますが、この2つの違いは何でしょうか。
「非ジェネリックとジェネリックの違いでしょ」と思いがちですが、実はそれだけの違いではありません。WeakReference には IsAlive Target というプロパティがありますが、WeakReference<T> にはそれらはなく、代わりに TryGetTarget メソッドが追加されています。
つまり、WeakReference と WeakReference<T> は、ジェネリクスか否かだけの違いではなく、メンバも変更されています。
WeakReference に潜む罠
とりあえず、WeakReference<T> のことは忘れて、WeakReference だけを考えます。
みなさんはどのようにして WeakReference からその参照先を取得していますか?
var ref = new WeakReference(new int[0]);
if (ref.IsAlive)
{
DoSomething(ref.Target as int[]);
}
説明するまでもないと思いますが、このコードは参照先が存在する場合にのみ DoSomething を呼び出すというコードです。
しかし、このコードには罠があります。本当に、DoSomething を呼び出す時点で参照先は存在していると言えるでしょうか。if の条件式を評価したあと、DoSomething を呼び出す前に GC が実行された場合、参照先はもはや存在しません。
このことを考慮すると、以下のようにするのが正しいことがわかります。
var ref = new WeakReference(new int[0]);
var obj = ref.Target as int[];
if (obj != null)
{
DoSomething(obj);
}
WeakReference<T>
では、WeakReference<T> ではどうでしょうか。WeakReference<T> には IsAlive も Target もなく、代わりに TryGetTarget があるだけですから、以下のように書くしかありません。
var ref = new WeakReference<int[]>(new int[0]);
int[] obj;
if (ref.TryGetTarget(out obj))
{
DoSomething(obj);
}
WeakReference にあった罠は、WeakReference<T> にはありません。
先述のとおり、WeakReference と WeakReferenc<T> では、メソッドやプロパティが異なっていますが、それは WeakReference に存在する罠が原因でしょう。そもそも、IsAlive で状態を取得したところで、次の瞬間には GC が実行されている可能性があるため、意味がありません。
まとめ
WeakReference ではなく WeakReference<T> をつかいましょう。ジェネリックでなく、罠が存在する WeakReference を使うメリットはありません。