2009-12-28 32 views
2

我有各種類包裝IntPtr。它們不存儲自己的數據(除了指針),而是使用屬性和方法使用非託管庫在指針中公開數據。它運行良好,但我已經到了需要能夠從其他包裝器對象中引用這些包裝器對象的地步。例如:我該如何避免在整個地方創建新的包裝對象?

public class Node { 
    private IntPtr _ptr; 

    public Node Parent { 
     get { return new Node(UnmanagedApi.GetParent(_ptr)); } 
    } 

    internal Node(IntPtr ptr) { 
     _ptr = ptr; 
    } 
} 

現在,我可以簡單地返回一個new Node(parentPtr)(如上),但對於有成千上萬節點的潛力。這不是一個壞主意,因爲多個包裝對象最終可能指向相同的IntPtr

我能做些什麼來解決這個問題?我想過使用一個靜態KeyedCollection類,它使用每個IntPtr作爲關鍵。所以,不要每次都返回一個新的Node,我可以查看它。但那會帶來線程問題,對吧?

有沒有更好的方法?

回答

1

我能看到的最大問題是誰負責刪除指針引用的對象?

重複使用同一個對象不一定是線程問題,但如果您負責調用非託管對象的delete,則需要在對象中實現某種引用計數。

如果您的對象是隻讀的,那麼使用多個具有相同指針的對象可能會更容易。如果他們的狀態可以改變,那麼如果多個對象持有指向該狀態的指針,則需要了解進行更改的影響。

您可能還想看看C++/CLI(託管C++),以在C#和非託管庫之間提供一個圖層,並在那裏完成翻譯/操作的艱苦工作,併爲C#提供更簡單的API 。

+0

非託管庫處理狀態。我的課程簡單地給它一個.NET外觀。不過,我明白你對刪除對象的看法。它們是一次性的,但後來我意識到,如果它們被存儲在「KeyValuePair」中,那麼這些對象無論如何都不會被垃圾收集。我所關心的是擁有數千個基本上都是基本相同的東西的開銷。它會甚至是顯而易見的,還是我只是爲了優化而優化? C++/CLI不是一種選擇,因爲我想稍後將它移植到Mono中。 – 2009-12-28 09:05:42

+0

一般來說,創建數千個對象會導致垃圾收集器出現一些令人頭疼的問題。 另一種選擇是傳遞指針,並有一個幫助類,它接受一個指針並返回所需的數據。例如Helper.GetIdFromUnmanagedObject(myPtr),而不是讓對象具有自己的行爲。從OO的角度來看,並不是那麼漂亮,但至少你只有非託管對象,而不是包裝。 – Paolo 2009-12-28 09:14:49

+0

是的,我基本上已經這樣做了(非託管API的構建有點像這樣),但我真的想用枚舉器將它與框架混合,以使其儘可能直觀。我想我會等待,看看它是如何發生的,並在實際上成爲問題時決定靜態引用集合。我仔細看了一眼,可能物體會很快被GC'd,因爲它們確實沒有任何指向其他物體的連接(因爲'IntPtrs'實際上是將它們連接在一起的)。 – 2009-12-28 09:22:00

0

我有一個相關的問題。刪除非託管對象是顯式完成的。所以我做的是爲所有包含可用包裝實例的靜態字典的包裝製作一個基類。對象被添加到構造函數的字典中,並在WrapperBase.Delete()方法中被刪除。請注意,爲這種方法使用顯式的Delete()方法非常重要 - 否則GC將永遠不會因靜態字典引用而釋放包裝器實例。

+0

我現在已經實現了IDisposable,所以我可以隨時從字典中刪除對象,當它被處理時。問題在於它完全取決於用戶。如果對象存儲在字典中,它將不會得到GC'd。我討厭讓用戶在數以萬計的對象上調用Dispose()或Delete()。 :( – 2009-12-28 09:07:50

1

這整個代碼看起來不正確。

您對GetParent函數的使用似乎意味着您有一個樹狀結構。讓我對你的代碼做一些猜測,他們可能是錯的。

  1. 您希望廣泛使用UnmanagedAPI並且不希望在.NET代碼中複製此代碼。

  2. 你只是想確保你不會因爲訪問你的代碼而導致內存問題。

我建議,而不是在一個節點到節點的基礎上創建.NET代碼,您可以爲整個樹/圖結構中的.NET包裝,並提供一個.NET API可以通過非託管API指針作爲參數,但嚴格處理分配/釋放,以避免內存問題。這將避免不必要地分配新的內存結構來簡單地分配已存在的內容,即GetParent函數。

相關問題