2011-04-14 151 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函数而不是属性。