我有以下情况。托管代码将初始化一个类的大量对象,该类是非托管结构的包装器。我可以为此做两种方法。一种是拥有一个托管类包装器,它只有一个指向非托管对象的指针。另一种方法是拥有一个完整的托管类,并在需要调用非托管方法时创建非托管对象。我提供了以下两种方法。有人告诉我,如果我使用方法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
}
};
答案 0 :(得分:1)
不,第一个片段是规范的方式。垃圾收集器只移动指针,它不移动指向的对象。那个应该用malloc()或new运算符分配,它不能被移动。
您的代码中还有几个严重问题。您似乎没有为A_UNMANAGED分配内存,除非GetData()通过引用获取其参数。永远不会调用GetData()。这通常必须是ref类(不是ref值),因此您可以提供析构函数和终结器来释放内存。 b
属性setter将使用StackOverflowException轰炸您的程序。在处理这个项目之前一定要学习这门语言。
检查this answer以获取示例代码。
答案 1 :(得分: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函数而不是属性。