考虑C ++ / CLI中的以下包装器,该包装器提供托管API并使用本机API(仅出于说明目的而使用C int):
public ref class A
{
public:
A()
{
native = new int;
*native = 0;
}
!A() // finalizer
{
delete native;
native = nullptr;
}
~A() // destructor
{
this->!A();
}
void Increase()
{
int *otherNative = native; // for illustrative purpose
// (1) Garbage collector might work here
*otherNative++; // (2) operation on native object
// (3)
}
private:
int *native;
};
在C#中考虑一个简单的客户端:
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Increase();
}
}
在代码中的(1)处,垃圾收集器可能会启动并收集 "不可到达的对象"。从书" Expert C ++ / CLI"作者:Marcus Heege(2007),也来自 https://blogs.msdn.microsoft.com/oldnewthing/20100813-00/?p=13153我了解到了这一点 "不可到达的对象"包括其唯一引用仍在范围内但不再使用的对象。 因此,在最后一个引用超出范围之前,可以对对象进行垃圾回收。 如果这让您感到困惑,请举例如下:
void Method()
{
Y y = SomehowCreateY();
int z = 123;
y->m();
z++; // "y" still in scope, but can be garbage collected here
blabla();
}
对于包装器示例,这意味着垃圾收集器可能会调用 在(1)和/或(2)期间或期间的终结器,因此本机对象将 在(2)中使用时或之前删除。
本书和博文都建议放GC.KeepAlive(this)
在示例代码中的(3)处以避免此问题。
所以基本上C ++ / CLI中对象活动的语义非常棘手,我们认为我们不能以这种方式编写可靠的包装器,因为我们很容易忘记编写GC.KeepAlive(this)
。
虽然我有很好的消息来源,这些棘手的语义确实存在,但这些来源 相当古老。我的问题: