個人的備忘録

調べたり思いついたりした事の置き場

ConditionalWeakTableを利用して拡張メソッドに状態を持たせる

拡張メソッドを利用してインタフェースに実装をもたせる場合、インタフェースが公開しているもの以外の状態は利用できない。 が、ConditionalWeakTableを利用することでそれに近いことが実現できる。

class Parameter
{
    public string str { get; set; }
}

interface IExtensible { }

static class Extensions
{
    static ConditionalWeakTable<IExtensible, Parameter> state = new ConditionalWeakTable<IExtensible, Parameter>();
    public static void SetStr(this IExtensible target, string str)
    {
        state.GetOrCreateValue(target).str = str;
    }
    public static string GetStr(this IExtensible target)
    {
        return state.GetOrCreateValue(target).str;
    }
}

こんな感じに利用する。
ConditionalWeakTableは連想配列のように働くのだが、Keyへの参照が弱参照で、他の参照がすべてなくなった場合GCにより回収される。 そして、その時ConditionalWeakTable内のレコードも削除されるのだそうだ。至れり尽くせりである。

ちなみに、ConditionalWeakTableのValueは参照型でなくてはいけない。設定のメソッドがないのでさもありなんである。 だが、実際は結構Enum等の値型を入れたいことがある。仕方がないのでこのようなクラスを使っている。

class Box<T>{
    public T Value { get; set; }
    public static implicit operator T(Box<T> target)
    {
        return target.Value;
    }
    public static implicit operator Box<T>(T target)
    {
        return new Box<T> { Value = target };
    }
}

今ひとつすっきりしないやり方だが、いい方法が見つからなかった。 他の局面でもこのような問題が出ることもあると思うのだが、何かもう少し綺麗なやり方はないのだろうか。