我在 C++ 中有一个观察者模式,我允许通过 C++/CLI 包装器从 C# 访问它。我发现它在垃圾收集方面没有按预期工作。我遇到了Call has been made on garbage collected delegate
错误,但据我所知,我坚持对委托的托管引用(通过 listeners_ 字典),所以我不明白为什么它会被 GC 处理。
在这里,我只是展示了 C++/CLI 包装器代码,它实现了与包装的 C++ 代码相同的接口(出于示例目的,我已将其放入“本机”命名空间中)。
我将非托管更新转发给托管委托的方式、我如何保留托管委托或我如何实现 addListener/removeListener 函数是否有问题?
using namespace System::Runtime::InteropServices;
using namespace System::Collections::Generic;
typedef boost::shared_ptr<native::IterationListener> IterationListenerPtr;
public ref struct IterationListener
{
enum class Status {Ok, Cancel};
ref struct UpdateMessage
{
UpdateMessage(int iterationIndex, int iterationCount, System::String^ message);
property System::String^ message;
property int iterationIndex;
property int iterationCount;
};
IterationListener();
virtual Status update(UpdateMessage^ updateMessage) {return Status::Ok;}
};
public delegate IterationListener::Status IterationListenerUpdate(IterationListener::UpdateMessage^ updateMessage);
#define DEFINE_INTERNAL_BASE_CODE(CLIType, NativeType) \
public: System::IntPtr void_base() {return (System::IntPtr) base_;} \
internal: CLIType(NativeType* base, System::Object^ owner) : base_(base), owner_(owner) {} \
CLIType(NativeType* base) : base_(base), owner_(nullptr) {} \
virtual ~CLIType() {if (owner_ == nullptr) {SAFEDELETE(base_);}} \
!CLIType() {delete this;} \
NativeType* base_; \
System::Object^ owner_; \
NativeType& base() {return *base_;}
public ref class IterationListenerRegistry
{
DEFINE_INTERNAL_BASE_CODE(IterationListenerRegistry, native::IterationListenerRegistry);
System::Collections::Generic::Dictionary<IterationListener^,
KeyValuePair<IterationListenerUpdate^, System::IntPtr> >^ _listeners;
public:
IterationListenerRegistry();
void addListener(IterationListener^ listener, System::UInt32 iterationPeriod);
void addListenerWithTimer(IterationListener^ listener, double timePeriod); // seconds
void removeListener(IterationListener^ listener);
IterationListener::Status broadcastUpdateMessage(IterationListener::UpdateMessage^ updateMessage);
};
IterationListener::IterationListener()
{
}
IterationListener::UpdateMessage::UpdateMessage(int iterationIndex,
int iterationCount,
System::String^ message)
{
this->iterationIndex = iterationIndex;
this->iterationCount = iterationCount;
this->message = message;
}
struct IterationListenerForwarder : public native::IterationListener
{
typedef IterationListener::Status (__stdcall *IterationListenerCallback)(IterationListener::UpdateMessage^);
IterationListenerCallback managedFunctionPtr;
IterationListenerForwarder(void* managedFunctionPtr)
: managedFunctionPtr(static_cast<IterationListenerCallback>(managedFunctionPtr))
{}
virtual Status update(const UpdateMessage& updateMessage)
{
if (managedFunctionPtr != NULL)
{
IterationListener::UpdateMessage^ managedUpdateMessage =
gcnew IterationListener::UpdateMessage(updateMessage.iterationIndex,
updateMessage.iterationCount,
ToSystemString(updateMessage.message));
return (Status) managedFunctionPtr(managedUpdateMessage);
}
return Status_Ok;
}
};
IterationListenerRegistry::IterationListenerRegistry()
{
base_ = new native::IterationListenerRegistry();
_listeners = gcnew Dictionary<IterationListener^, KeyValuePair<IterationListenerUpdate^, System::IntPtr> >();
}
void IterationListenerRegistry::addListener(IterationListener^ listener, System::UInt32 iterationPeriod)
{
IterationListenerUpdate^ handler = gcnew IterationListenerUpdate(listener, &IterationListener::update);
IterationListenerPtr forwarder(new IterationListenerForwarder(Marshal::GetFunctionPointerForDelegate(handler).ToPointer()));
_listeners->Add(listener, KeyValuePair<IterationListenerUpdate^, System::IntPtr>(handler, System::IntPtr(&forwarder)));
base().addListener(forwarder, (size_t) iterationPeriod);
}
void IterationListenerRegistry::addListenerWithTimer(IterationListener^ listener, double timePeriod)
{
IterationListenerUpdate^ handler = gcnew IterationListenerUpdate(listener, &IterationListener::update);
IterationListenerPtr forwarder(new IterationListenerForwarder(Marshal::GetFunctionPointerForDelegate(handler).ToPointer()));
_listeners->Add(listener, KeyValuePair<IterationListenerUpdate^, System::IntPtr>(handler, System::IntPtr(&forwarder)));
base().addListenerWithTimer(forwarder, timePeriod);
}
void IterationListenerRegistry::removeListener(IterationListener^ listener)
{
base().removeListener(*static_cast<native::IterationListenerPtr*>(_listeners[listener].Value.ToPointer()));
_listeners->Remove(listener);
}
IterationListener::Status IterationListenerRegistry::broadcastUpdateMessage(IterationListener::UpdateMessage^ updateMessage)
{
std::string message = ToStdString(updateMessage->message);
native::IterationListener::UpdateMessage nativeUpdateMessage(updateMessage->iterationIndex,
updateMessage->iterationCount,
message);
return (IterationListener::Status) base().broadcastUpdateMessage(nativeUpdateMessage);
}