Ich bin mir bewusst, dass Finalizer in der Regel verwendet werden, um nicht verwaltete Ressourcen zu kontrollieren. Unter welchen Umständen kann ein Finalizer mit verwalteten Ressourcen umgehen?
Nach meinem Verständnis verhindert das Vorhandensein in der Finalizer-Warteschlange, dass ein Objekt oder Objekte, auf die stark verwiesen wird, abgeholt werden, aber es schützt sie (natürlich) nicht vor der Finalisierung. Sobald ein Objekt abgeschlossen ist, wird es aus der Warteschlange entfernt, und alle Objekte, auf die es verweist, sind beim nächsten GC-Durchlauf nicht mehr vor der Abholung geschützt. Wenn ein Finalizer aufgerufen wird, können die Finalizer für jede beliebige Kombination von Objekten, auf die das Objekt verweist, aufgerufen worden sein; man kann sich nicht darauf verlassen, dass die Finalizer in einer bestimmten Reihenfolge aufgerufen werden, aber die Objektreferenzen, die man besitzt, sollten noch gültig sein.
Es ist ziemlich klar, dass ein Finalizer niemals Sperren erwerben oder versuchen darf, ein neues Objekt zu erstellen. Nehmen wir jedoch an, dass ich ein Objekt habe, das einige Ereignisse abonniert, und ein anderes Objekt, das die Ereignisse tatsächlich verwendet. Wenn das letztere Objekt für die Garbage Collection in Frage kommt, möchte ich, dass sich das erstere Objekt so schnell wie möglich von den Ereignissen abmeldet. Beachten Sie, dass das erstgenannte Objekt erst dann für die Finalisierung in Frage kommt, wenn es von keinem lebenden Objekt mehr abonniert wird.
Wäre es praktisch, einen sperrfreien verknüpften Listenstapel oder eine Warteschlange von Objekten zu haben, die abgemeldet werden müssen, und den Finalizer des Hauptobjekts einen Verweis auf das andere Objekt auf den Stapel/die Warteschlange legen zu lassen? Das Objekt der verknüpften Liste müsste bei der Erstellung des Hauptobjekts zugewiesen werden (da die Zuweisung innerhalb des Finalisierers verboten wäre), und es wäre wahrscheinlich notwendig, etwas wie ein Timer-Ereignis zu verwenden, um die Warteschlange abzufragen (da die Abmeldung des Ereignisses außerhalb des Finalisierer-Threads erfolgen müsste), und es wäre wahrscheinlich albern, einen Thread zu haben, dessen einziger Zweck es ist, darauf zu warten, dass etwas in der Finalizer-Warteschlange erscheint), aber wenn der Finalizer sicher auf sein zuvor zugewiesenes Linked-List-Objekt und das mit seiner Klasse verbundene Haupt-Warteschlangenobjekt verweisen könnte, könnte er es ermöglichen, dass die Ereignisse innerhalb von 15 Sekunden oder so nach der Finalisierung abbestellt werden.
Wäre das eine gute Idee? (Anmerkungen: Ich verwende .net 2.0; außerdem könnte ein Versuch, dem Stapel oder der Warteschlange etwas hinzuzufügen, ein paar Mal auf Threading.Interlocked.CompareExchange drehen, aber ich würde nicht erwarten, dass es jemals sehr lange stecken bleiben sollte).
EDIT
Natürlich sollte jeder Code, der Ereignisse abonniert, iDisposable implementieren, aber Einwegartikel werden nicht immer richtig entsorgt. Wenn es so wäre, gäbe es keine Notwendigkeit für Finalizer.
Mein Szenario wäre etwa folgendes: eine Klasse, die iEnumerator(of T) implementiert, hängt sich an ein changeNotify-Ereignis ihrer zugehörigen Klasse, so dass eine Aufzählung sinnvoll behandelt werden kann, wenn sich die zugrundeliegende Klasse ändert (ja, ich weiß, dass Microsoft der Meinung ist, dass alle Enumeratoren einfach aufgeben sollten, aber manchmal ist ein Enumerator, der weiter funktionieren kann, nützlicher). Es ist durchaus möglich, dass eine Instanz der Klasse im Laufe von Tagen oder Wochen viele tausend oder sogar Millionen Mal aufgezählt wird, aber in dieser Zeit überhaupt nicht aktualisiert wird.
Im Idealfall würde der Enumerator nie vergessen werden, ohne dass er entsorgt wird, aber Enumeratoren werden manchmal in Kontexten verwendet, in denen "foreach" und "using" nicht anwendbar sind (z.B. unterstützen einige Enumeratoren verschachtelte Aufzählungen). Ein sorgfältig entworfener Finalizer könnte eine Möglichkeit bieten, mit diesem Szenario umzugehen.
Im Übrigen würde ich verlangen, dass jede Aufzählung, die durch Aktualisierungen fortgesetzt werden soll, die generische IEnumerable(of T) verwenden muss; die nicht-generische Form, die nicht mit iDisposable umgehen kann, müsste eine Ausnahme auslösen, wenn die Sammlung geändert wird.