はじめに
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
を使うメリットはありません。