C#/。NETで、弱参照が指すオブジェクトが破棄される前に通知を受け取る方法はありますか?基本的に、オブジェクトの収集を許可したいのですが、デストラクタを追加するためにコードを変更せずに、オブジェクトが破棄される直前に何かを実行します(コードでどのタイプのオブジェクトが訴えられるか正確にはわからないため)。
ありがとう、ロバート
C#/。NETで、弱参照が指すオブジェクトが破棄される前に通知を受け取る方法はありますか?基本的に、オブジェクトの収集を許可したいのですが、デストラクタを追加するためにコードを変更せずに、オブジェクトが破棄される直前に何かを実行します(コードでどのタイプのオブジェクトが訴えられるか正確にはわからないため)。
ありがとう、ロバート
.Net 4.0 has the solution you need: ConditionalWeakTable. Here is a short program that demonstrates the idea. (discussed here as well)
using System;
using System.Runtime.CompilerServices;
namespace GCCollectNotification
{
class ObjectToWatch { }
class Notifier
{
public object ObjectToWatch { get; set; }
~Notifier() { Console.WriteLine("object is collected"); }
}
class Program
{
private ConditionalWeakTable<object, Notifier> map
= new ConditionalWeakTable<object, Notifier>();
public void Test()
{
var obj = new ObjectToWatch();
var notifier = map.GetOrCreateValue(obj);
notifier.ObjectToWatch = obj;
}
static void Main(string[] args)
{
new Program().Test();
GC.Collect();
GC.WaitForPendingFinalizers();
// "object is collected" should have been printed by now
Console.WriteLine("end of program");
}
}
}
あなたはそれをすることはできません。ただし、実行できるのは、GCが近づいていることを監視することです(CLRv3.5Sp1にはこれを可能にする新しいGCAPI、GCNotificationsがあります)
この機能を実現する方法はありません。
少し推測した後、あなたが説明している方法で機能を実装することは不可能だと思います。
WeakReferenceによって保持されているオブジェクトが収集された時点で、それ以上の参照がないことを考慮してください(したがって、収集可能です)。イベントを使用するには、イベントの一部としてオブジェクトを提供する必要があります。これは、参照が収集可能から収集不可能になったことを意味します。処理コードがそのオブジェクトの参照を再取得するのを止めるものは何もありません。したがって、オブジェクトは収集可能とは見なされなくなります。CLRは、オブジェクトが収集可能であることを再確認するために、オブジェクトに対して2回目のパスを作成する必要があります。
収集できないオブジェクトにつながるため、イベントの2回目が発生しなかったことがわかります。
オブジェクトが収集される直前にこのイベントが発生したと主張するのは、名前の誤用です。ハンドラーがオブジェクトへの新しい参照を確立することによってこれが収集されるのを防ぐことができるという理由だけで。代わりに、「ObjectMaybeAboutToBeCollected」である必要があります。これはおそらくあなたが探している振る舞いをあなたに与えないでしょう。
あなたの質問は私には意味がありません。呼び出されるコードはどこにあるはずですか? 参照されたオブジェクトが破棄される前に弱参照が null になることを考えると、それが破棄されようとしているオブジェクトを参照したクラスの一部であることは意味がありません。そして、オブジェクトが破棄される前に呼び出されるコードが、参照先のオブジェクトに既に存在します。これがデストラクタです。
解決したい実際の設計上の問題は何ですか? もっと良い方法があるかもしれません。
ノーティファイアを使用した弱い参照がファイナライザーを使用したオブジェクトと同様に見なされた場合、つまり、オブジェクトがもはや誰にとっても関心がないと見なされた場合、それはファイナライズと通知のためにキューに入れられます。キュー エントリはライブ参照と見なされるため、オブジェクトは実際に処理されるまで収集されません。
それは不可能であるため、実行可能な最善のアプローチは、すべての「このオブジェクトに興味があります」参照を、実際のオブジェクトを指す軽量ラッパー オブジェクトを指し、「弱い」参照を持つことです。実際のオブジェクトも指す別のラッパーを指します。最初のラッパーは 2 番目のラッパーへの参照を保持する必要がありますが、その逆はできません。最初のラッパーには、スコープ外になったときに適切なコードをトリガーするファイナライザーが必要です。
残念ながら、そのような戦略の完全な実装は見たことがありません。考慮すべき重要な注意事項がいくつかあります。(1) ファイナライザーは、ロックを待機したり、例外をスローする可能性のあることを行ったりしてはなりません。(2) 範囲外になった可能性のある他のオブジェクトにアクセスするコードは、それらが既にファイナライズされている、ファイナライズ中、ファイナライズを待っている、またはまだ他の場所でライブ参照を持っている可能性に備えて準備する必要があります。(3) ファイナライザが、ガベージ コレクションの対象となるファイナライズ可能なオブジェクトへのルート化された参照を格納する場合、ライブ参照が存在する場合でも、そのようなオブジェクトはファイナライズされる可能性があります。
あなたが説明していることについては、ファイナライザーがより良いアプローチになるでしょう。