0

在一些 C++/CLI 代码中,我有一个本地类,它有一个工厂方法GetWrapper()来创建自己的托管 .NET 包装对象。在内部,它通过 GCHandle 持有对其包装器的弱引用。当GetWrapper()被调用时,检查 GCHandle 并返回现有包装器的句柄,或者(如果它不再指向对象,因为旧包装器对象已被垃圾收集器销毁)创建一个新包装器回来。

// .h
class NativeClass
{
public:
    WrapperClass^ GetWrapper();
private:
    WrapperClass^ GetNewWrapper();
    GCHandle m_wrapperGCHandle;
};

// .cpp
WrapperClass^ NativeClass::GetWrapper()
{
    if(m_wrapperGCHandle.IsAllocated)
    {
        try
        {
            WrapperClass^ wrapper = nullptr;
            wrapper = dynamic_cast<WrapperClass^>(wrapperGCHandle.Target);

            if(wrapper == nullptr)
            {
                return GetNewWrapper();
            }
            else
            {
                return wrapper;
            }
         }
         catch(System::InvalidOperationException^)
         {
             return GetNewWrapper();
         }
    else
    {
        return GetNewWrapper();
    }
}

WrapperClass^ NativeClass::GetNewWrapper()
{
    WrapperClass^ wrapper = gcnew WrapperClass(/*some args*/);
    m_wrapperGCHandle = GCHandle::Alloc(wrapper, GCHandleType::Weak);
}

现在奇怪的是它m_wrapperGCHandle.IsAllocated 总是返回 true,即使包装器已被垃圾收集。MSDN告诉“在使用弱句柄时使用此属性来确定 GCHandle 是否仍然可用。”。但这总是正确的。如果它不可用,那么 Target 是一个 nullptr。

我错过了什么还是 MSDN 错了?

4

1 回答 1

2

我对 MSDN doco 的阅读是,在被调用m_wrapprGCHandle.IsAllocated之前将返回 true - 该属性正在检查句柄的状态,而不是句柄持有的引用的状态。m_wrapperGCHandle.FreeIsAllocated

正如您所指出的,m_wrapperGCHandle.Target当对象已被垃圾收集时,它是 null 。我使用了与示例代码中类似的方法来生成托管包装器类,我总是检查是否为空,如果Target为空,则重新生成包装器对象Target

另外,一个建议……在我看来,您的代码中好像有句柄泄漏,因为您在GCHandle::Alloc没有相应调用的情况下进行m_wrapperGCHandle.Free调用。尝试将调用Alloc放在类构造函数中并将调用Free放在析构函数中:

NativeClass::NativeClass()
{
    m_wrapperGCHandle = GCHandle::Alloc(nullptr, GCHandleType::Weak);
}

NativeClass::~NativeClass()
{
    m_wrapperGCHandle.Free();
}

那么你的GetNewWrapper方法很简单:

WrapperClass^ NativeClass::GetNewWrapper()
{
    m_wrapperGCHandle.Target = gcnew WrapperClass(/*some args*/);
}

您可以从方法中删除if(m_wrapperGCHandle.IsAllocated) - elseGetWrapper

于 2012-05-11T13:43:07.570 回答