C ++ / CLI:在C#.NET应用中返回对非托管对象的引用

时间:2019-05-03 08:51:09

标签: c# c++ c++-cli

我正在使用C ++ / CLI将某些功能从本机C ++ dll打包到.NET。我的一个本机类(UA类)具有一种方法,该方法返回对另一个本机类/对象(UB类)的引用。我在我的C ++ / CLI项目中都包装了两个类,如下所示:

//Unmanaged classes
class UA 
{
public:
A(int x) : m_x(x) {}
int m_x;
};

class UB 
{
public:
UB() : a(1) {}
UA& get_A(){return a;}
protected:
UA a;
};

// managed classes /CLI
public ref class A
{
internal:
A(UA* a) : m_ptr(a) {}
public:
A() : m_ptr(new UA(1)) {}
~A()
{
  this->!A();
}
!A()
{
  if (m_ptr != nullptr)
  {
     delete m_ptr;
  }
}
private:
UA* m_ptr;
};

public ref class B 
{
public:
B() : m_ptr(new UB()) {}
A^ get_A(){return gcnew A(&m_ptr->get_A())}
~B()
{
  this->!B();
}
!B()
{
  if (m_ptr != nullptr)
  {
     delete m_ptr;
  }
}
private:
UB* m_ptr;
};

以上内容使用/clr进行编译;但是,在C#.NET应用程序中测试时(请参见上文),我收到以下调试声明错误:

// C#.NET app
B b = new B();
A = b.get_A();

Assertion error in finalizer

此错误发生在托管包装(!A())的终结器class A中。特别是,当GC尝试删除本机指针时,会引发此错误。我想这只是为什么我没有使用new关键字创建此实例。您是否认为最好在本机C ++中使用副本构造函数,然后使用new关键字创建托管包装类?

例如:

//Unmanaged classes
class UA 
{
public:
A(int x) : m_x(x) {}
A(const A& a)
{
  m_x = a.m_x;
}
int m_x;
};

然后托管的A和B包装器将是:

// managed classes /CLI
public ref class A
{
internal:
A(UA* a) : m_ptr(a) {}
public:
A() : m_ptr(new UA(1)) {}
~A()
{
  this->!A();
}
!A()
{
  if (m_ptr != nullptr)
  {
     delete m_ptr;
  }
}
private:
UA* m_ptr;
};

public ref class B 
{
public:
B() : m_ptr(new B()) {}
A^ get_A(){return gcnew A(new UA(&m_ptr->get_A()))}
~B()
{
  this->!B();
}
!B()
{
  if (m_ptr != nullptr)
  {
     delete m_ptr;
  }
}
private:
UB* m_ptr;
};

是否有其他方法可以返回对C#中非托管对象的引用?抱歉,如果我做一些根本错误的事情,那么我对C ++ / CLI的经验将很少。我也看到了这个post;但是,似乎已被接受的答案提出了我首先实施的解决方案。 感谢您的帮助/建议。

1 个答案:

答案 0 :(得分:0)

这仅仅是由于这样的事实,当您调用b.get_A()时,它会调用内部构造函数A(UA* a) : m_ptr(a) {},该构造函数仅存储指向类型为{{1的对象的字段a}的指针}}。然后,析构函数将尝试删除无法使用的指针。

这与C ++ / CLI无关,只是与分配和删除对称。在这种情况下,只需删除您的公共构造函数UB即可进行分配,并删除那些析构函数,它将起作用。