2009-07-17 114 views
8

在C#/ .NET中,是否有任何方式在弱引用指向的對象被破壞之前獲取通知?基本上,我想允許收集一個對象,但是在對象被銷燬之前做一些事情,而不需要修改代碼來添加析構函數(因爲我不知道哪些類型的對象會被我的代碼起訴)。C#:收集WeakReference之前的通知?

感謝, 羅伯特

回答

5

你不能那樣做。 但是,您可以做的是觀察GC何時正在接近(CLR v3.5Sp1中有新的GC API,允許您這樣做,GCNotifications)

6

沒有就沒有辦法來實現這一功能。

經過一番猜測之後,我不認爲有可能以您描述的方式實現一個功能。

考慮到在收集WeakReference所持有的對象時,沒有更多的引用(因此它是可收集的)。如果某個事件對您有任何用處,則需要提供該對象作爲事件的一部分。這意味着參考文獻從可收集到不可收集。沒有任何東西可以阻止處理代碼重新獲得對該對象的引用。因此,該對象不能再被認爲是可收集的。 CLR需要對該對象進行第二次傳遞才能重新確保它是可收集的。

您可以看到事件的第二次無法提升,因爲它會導致無法收集的對象。

在收集對象之前提出這個事件是由於濫用命名而導致的。只是因爲任何處理程序都可以通過建立對該對象的新引用來阻止它被收集。相反,它必須是「ObjectMaybeAboutToBeCollected」。這可能不會給你你正在尋找的行爲。

+0

很酷,謝謝...你能想出任何方法來獲得除WeakReferences以外的類似功能嗎? – 2009-07-17 20:39:18

+0

@羅伯特,除了形式的析構/處置我不能想到任何事情我的頭頂 – JaredPar 2009-07-17 20:40:23

+0

我絕對同意,無論如何,弱事件可能會做的伎倆!一個小小的捕獲,他們不存在於C#中,所以你必須實現它(這有點棘手,但基本上可能)。 – 2010-12-12 12:16:48

0

你的問題對我沒有意義。代碼將被稱爲應該駐留在哪裏?考慮到弱引用將在被引用對象被銷燬之前被清空,所以引用即將被銷燬的對象的類的一部分是沒有意義的。在對象被銷燬之前,被引用對象中已經存在代碼 - 這就是析構函數。

你想解決的實際設計問題是什麼?可能有更好的方法。

0

對於您所描述的內容,終結器將會更好做法。

0

如果帶有通告符的弱引用被認爲與帶有終結符的對象類似,也就是說當對象被認爲不再感興趣時,可能會有類似於您描述的語義任何人,它將排隊等待最終確定和通知;該隊列條目將被視爲一個實時參考,因此該對象在實施之前不會實際收集。

因爲這是不可能的,所以最好的可行方法可能是讓所有「我對這個對象感興趣」的引用指向一個輕量級的包裝對象,它將轉而指向真實的對象, 「弱」引用指向另一個也會指向真實對象的包裝器。第一個包裝應該對第二個包裝提供參考,但反之亦然。第一個包裝器應該有一個終結器,當它超出範圍時會觸發適當的代碼。

不幸的是,我還沒有看到這種策略的任何完整實現。有一些重要的注意事項需要考慮。其中:(1)終結者不應該等待鎖定,也不應該做任何可以拋出異常的東西; (2)訪問可能已經超出範圍的其他對象的代碼必須準備好,以使它們可能已經最終確定,正在定案過程中,等待最終確定,或者在其他地方仍有實時參考。 (3)如果終結器存儲了對已被認定有資格進行垃圾收集的可終結對象的根引用,則即使存在實時引用,也可以確定這樣的對象。

6

.net 4.0有您需要的解決方案:ConditionalWeakTable。這是一個簡短的程序,展示了這個想法。 (也討論了here

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"); 
     } 
    } 
}