2011-04-14 73 views
3

我有以下情況。託管代碼將初始化很多類的對象,這是一個非託管結構的包裝。我可以爲此做兩種方法。一個是擁有一個託管類包裝器,它只有一個指向非託管對象的指針。另一個是擁有一個完整的託管類,並在需要調用非託管方法時創建非託管對象。我提供了以下兩種方法。有人告訴我,如果我使用方法1(有一個指向無人管理對象的指針),GC將會有很多問題知道非託管部分,最好採取方法2。有人告訴我哪個更好,或者如果有是另一種更好的方法。我對方法2的關注是,每次調用非託管方法時都會複製。我不確定GC問題是否勝過它。高效地調用非託管方法將非託管對象作爲託管代碼的參數

編輯 - 第一種方法有一個ref類,第二種方法有一個值類。究其原因,第二個是值,使得其可以更有效地

添加到列表在非託管:

struct A_UNMANAGED 
{ 
    int a; 
    int b[20]; 
}; 

void GetData(A_UNMANAGED& a); // populates A 

在管理(第一種方法)

public ref class A_MANAGED 
{ 
    A_UNMANGED* ap; 

public: 
    property System::UInt32 a 
    { 
     System::UInt32 get() { return ap->a; } 
     void set(System::UInt32 value) { ap->a = value; } 
    } 

    property array<System::UInt32>^ b 
    { 
     array<System::UInt32>^ get() { return ap->b; } 
     void set(array<System::UInt32>^ value) { b = value; } // assume this copy works 
    } 

internal: 
    void GetData() 
    { 
     GetData(ap); 
    } 
}; 

在託管(第二種方法)(編輯:更新爲參考假設所有垃圾收集和指針創建寫入正確)

public value class A_MANAGED 
{ 
    System::UInt32 a; 
    array<System::UInt32>^ b; 

public: 
    property System::UInt32 a 
    { 
     System::UInt32 get() { return a; } 
     void set(System::UInt32 value) { a = value; } 
    } 

    property array<System::UInt32>^ b 
    { 
     array<System::UInt32>^ get() { return b; } 
     void set(array<System::UInt32>^ value) { b = value; } 
    } 

internal: 
    void GetUnmanaged(A_UNMANAGED& obj1) 
    { 
     obj1.a = a; 
     pin_ptr<System::UInt32> bp = &b[0]; 
     memcpy(obj1.b, bp, 20); 
    } 

    void GetData() 
    { 
     A_UNMANAGED obj2; 
     GetUnmanaged(obj2); 
     GetData(obj2); 
     // copy from obj2 to member variables 
    } 
}; 

回答

1

不,第一小節它是規範的方式。垃圾回收器只移動指針,它不會移動指向的對象。那個應該已經分配了malloc()或new運算符,它不能被移動。

在你的代碼中還有幾個嚴重的問題。您似乎沒有爲A_UNMANAGED分配內存,除非GetData()通過引用接受其參數。 GetData()永遠不會被調用。這通常必須是ref類(不是ref值),因此您可以提供析構函數和終結器來釋放內存。 b屬性設置器會用StackOverflowException拋出程序。在處理這個項目之前一定要學習這門語言。

檢查this answer的示例代碼。

+0

我打算標記第一級參考。我確實有析構函數和終結器,但沒有寫在這裏。也正如我所指定的假設b副本的作品。我的問題是要知道哪種方法更好,如果GC和價值問題是真的 – 2011-04-14 01:07:31

+2

那麼,你通過發佈蹩腳的代碼浪費了我的時間。這麼做的意義何在?它不會改變我的答案。 – 2011-04-14 01:10:28

1

正如漢斯所說,第一種方法是通常的方法(儘管個人而言,我認爲P/Invoke在這種情況下會更加簡潔...)。但是,您的A_MANAGED::b實現將不起作用,如果試圖簡單地編譯它,這將是顯而易見的。試試這個:

public ref class A_MANAGED 
{ 
    A_UNMANAGED* ap; 

public: 
    A_MANAGED() : ap(new A_UNMANAGED()) { } 
    ~A_MANAGED() { this->!A_MANAGED(); } 
    !A_MANAGED() { delete ap; ap = nullptr; } 

    property int a 
    { 
     int get() { return ap->a; } 
     void set(int value) { ap->a = value; } 
    } 

    property array<int>^ b 
    { 
     array<int>^ get() 
     { 
      using System::Runtime::InteropServices::Marshal; 
      array<int>^ arr = gcnew array<int>(20); 
      Marshal::Copy(System::IntPtr(ap->b), arr, 0, 20); 
      return arr; 
     } 
     void set(array<int>^ value) 
     { 
      using System::Runtime::InteropServices::Marshal; 
      Marshal::Copy(value, 0, System::IntPtr(ap->b), 20); 
     } 
    } 

internal: 
    void GetData() 
    { 
     ::GetData(*ap); 
    } 
}; 

然後還有約從性質返回數組通常警告:這是一個糟糕的想法。除非你真的想保持與非託管類的公共接口的平價,b應該真的是一對set/get函數而不是屬性。