9

不変オブジェクトを含むいくつかの状況では、意味的に同一である多くの別個のオブジェクトが存在する可能性があります。簡単な例は、ファイルから文字列にテキストの多くの行を読み取ることです。プログラムの観点からは、2行が同じ文字シーケンスを持っているという事実は「一致」ですが、プログラマーの観点からは、大量の重複が予想される場合があります。多くの文字列インスタンスが同一である場合、それらの個別のインスタンスへの参照を単一のインスタンスへの参照に変更すると、メモリが節約され、それらの間の比較も容易になります(2つの文字列参照が同じ文字列を指している場合、文字を入力する必要はありません-それらが同一であることを決定するための文字ごとの比較)。

一部のシナリオでは、システムが提供する文字列インターン機能が役立つ場合があります。ただし、いくつかの厳しい制限があります。

  1. 文字列がインターンされると、そのインターンされたコピーは、その文字列への他の参照が存在するかどうかに関係なく、永久に存続します。
  2. 文字列インターン機能は文字列でのみ機能し、他の不変のタイプでは使用できません。

trueが存在する場合WeakDictionary<ImmutableClassType, ImmutableClassType>(各要素について、キーと値は同一になります)、コードは次のようになります。

if (theDict.TryGetValue(myString, ref internedString))
  myString = internedString;
else
  theDict[myString] = myString;

残念ながら、私はWeakDictionary<keyType, valType>.netに組み込まれているクラスを知りません。さらに、両方の参照が常に同じものを指している場合、各アイテムのキーと値の弱参照を生成するのは無駄に思えます。

についていくつか読んだConditionalWeakTableことがありますが、それは興味深いクラスのように聞こえますが、目的は1つのインスタンスを取得して、意味的に同等の別の独立したインスタンスを見つけることができるため、ここでは使用できないと思います。

クラスのインスタンスの有効期間が明確に定義されている状況では、従来の方法を使用してDictionary同一のインスタンスをマージするのが妥当な場合があります。Dictionaryただし、多くの場合、そのようなものをいつ放棄するか、またはその中のアイテムをクリーンアップする必要があるかを知るのは難しい場合があります。ベースのWeakReferenceインターンコレクションは、そのような問題を回避します。そのようなものは存在しますか、それとも簡単に実装できますか?

補遺 svickが指摘したように、ライブでターゲットの値を返し、デッドでその値を返し続けるをDictionary<WeakReference, WeakReference>定義する実用的な方法がないため、aはやや問題があります。整数のターゲットハッシュコード値(コンストラクターで設定)を含み、その整数を返す構造体を定義できます。わずかな改善は、を使用して、削除のためにテーブルアイテムをキューに入れるために使用できるファイナライズ可能なオブジェクトにターゲットをリンクすることです。IEqualityComparerWeakReferenceGetHashCodeGetHashCodeConditionalWeakTableWeakReference

辞書を熱心にクリーンアップしようとすることと、やや受動的なアプローチを取ること(たとえば、最後のスイープ以降に少なくとも1つのGCがあった場合にアイテムを追加するときにスイープを実行することと、最後のスイープ以降に追加されたアイテムの数が、それを生き残ったアイテムの数を超えています)。辞書内のすべてをスイープすることは無料ではありませんが、ConditionalWeakTableもおそらく無料ではありません。

PPS 私が考えていた別の概念ですが、弱いインターンのアプローチほど有用ではなく、論理的に不変の型に可変の「タイムスタンプ」値​​を保持させ、によるその引数ref。2つの異なるインスタンスが等しいことが判明した場合、それらのタイムスタンプ値が調べられます。両方がゼロの場合、グローバルカウンター(-1、-2、-3など)から連続した負の数が割り当てられます。低いタイムスタンプ値を持っていた(または割り当てられた)パラメータは、他のパラメータに置き換えられます。

このようなアプローチを使用すると、多くのオブジェクトインスタンスが繰り返し比較された場合、参照の多くが「古い」インスタンスへの参照に置き換えられます。使用パターンによっては、これにより、インターンディクショナリを使用せずに、ほとんどの同一のオブジェクトインスタンスがマージされる場合があります。ただし、ネストされたオブジェクトにこのようなアプローチを適用するには、「不変」オブジェクトを使用して、ネストされたオブジェクトの参照を変更して、他のおそらく同一のネストされたオブジェクトを指すようにする必要があります。「おそらく同一の」オブジェクトが常にそうである場合、これは問題ないはずですが、そうでない場合、かなり奇妙な誤動作を引き起こす可能性があります。

4

1 に答える 1

3

カスタム等値比較子のようなものを作成Dictionary<WeakReference, WeakReference>し、適切な時期に生きていないものを取り除くことができます。これに関する問題の 1 つは、deadWeakRefrenceをディクショナリから削除する方法です。これは、参照の等価性 (カスタムの等価比較子を使用する必要があることを思い出してください) またはインデックスでは削除できないためです。WeakReference考えられる解決策は、参照が死んでいても、正しいハッシュ コードを継承する型を作成することです。または、カスタムでラップすることもできますstruct

しかし、あなたが言ったように、各参照が死んだ直後に辞書から削除されるといいでしょう。これを行う唯一の方法は、どういうわけかファイナライザーを使用することだと思います。しかし、ディクショナリの型を変更したくない (または変更できない) 場合、これは非常に複雑になります。

基本的な考え方は、上記と同じ弱参照のディクショナリを持つことです (アイテムを削除する方法に関する警告は引き続き適用されます) が、ConditionalWeakTable. そして、そのファイナライザーで、辞書からアイテムを削除します。仕組みConditionalWeakTable上、ディクショナリ内のアイテムが GCed になると、アタッチされたオブジェクトも同様に処理されます。つまり、そのファイナライザーが呼び出され、アイテムがディクショナリから削除されます。

于 2012-06-09T18:18:45.453 に答える