This is a simplified version of code I have that is not cleaning up in the expected order. This sample code causes an access violation at exit since a native object being wrapped is deleted before it can be unsubscribed from.
The scenario is that a C# client is creating two concrete CLI objects and storing them as interface handles. Then one concrete object is set up to store a reference to the other, again via an interface handle. This relationship is set up to provide managed wrappers around two classes where one subscribes to the other at the native level.
I would expect that e.g. object A holding a reference to object B would prevent object B from being finalized/disposed? This is not what I am observing. Is there a better way to represent the relationship so that the clean up is correct?
Native Classes:
#pragma managed(push, off)
class CNative2;
//------------------------------------------------------------------------------
class CNative1
{
public:
CNative1(){}
~CNative1(){}
开发者_如何学Python
void Subscribe( CNative2* pn2 )
{
m_pn2 = pn2;
}
void Unsubscribe()
{
m_pn2 = 0;
}
CNative2* m_pn2;
};
//------------------------------------------------------------------------------
class CNative2
{
public:
CNative2(){}
~CNative2()
{
m_pn1->Unsubscribe();
}
void Initialize( CNative1* pn1 )
{
m_pn1 = pn1;
m_pn1->Subscribe( this );
}
private:
CNative1* m_pn1;
};
#pragma managed(pop)
CLI Class Library Code:
#include "Native.h"
using namespace System;
namespace CLILib
{
//------------------------------------------------------------------------------
public interface class Interface1
{
};
//------------------------------------------------------------------------------
public interface class Interface2
{
void Initialize( Interface1^ i1 );
};
//------------------------------------------------------------------------------
public ref class Class1 : Interface1
{
public:
Class1()
{
m_pn1 = new CNative1();
};
~Class1()
{
this->!Class1();
}
!Class1()
{
delete m_pn1;
}
internal:
CNative1* GetNative()
{
return m_pn1;
}
private:
CNative1* m_pn1;
};
//------------------------------------------------------------------------------
public ref class Class2 : Interface2
{
public:
Class2()
{
m_pn2 = new CNative2();
}
~Class2()
{
this->!Class2();
}
!Class2()
{
delete m_pn2;
}
virtual void Initialize( Interface1^ i1 )
{
m_i1 = i1;
Class1^ c1 = safe_cast< Class1^ >( i1 );
m_pn2->Initialize( c1->GetNative() );
}
Interface1^ m_i1;
private:
CNative2* m_pn2;
};
}
C# Client Code:
using CLILib;
namespace ManagedClient
{
class Program
{
static void Main( string[] args )
{
for ( int i = 0; i < 1000000; ++i )
{
Interface1 i1 = new Class1();
Interface2 i2 = new Class2();
i2.Initialize( i1 );
}
}
}
}
Well, I kept working on it and resolved the issue by firing an event from the finalizer of one object to notify the other to clean up it's native object before the first finalizer completes. It feels a bit dirty doing it in the finalizer. is that bad?
// CLILib.h
#pragma once
#include "Native.h"
using namespace System;
namespace CLILib
{
interface class Interface2;
//------------------------------------------------------------------------------
public interface class Interface1
{
};
//------------------------------------------------------------------------------
public interface class Interface2
{
void Initialize( Interface1^ i1 );
};
//------------------------------------------------------------------------------
public ref class Class1 : Interface1
{
public:
Class1()
{
m_pn1 = new CNative1();
};
~Class1()
{
this->!Class1();
}
!Class1()
{
OnEvent();
delete m_pn1;
}
delegate void Eventhandler();
event Eventhandler^ OnEvent;
internal:
CNative1* GetNative()
{
return m_pn1;
}
private:
Interface2^ m_hi2;
CNative1* m_pn1;
};
//------------------------------------------------------------------------------
public ref class Class2 : Interface2
{
public:
Class2()
{
m_pn2 = new CNative2();
}
~Class2()
{
this->!Class2();
}
!Class2()
{
}
virtual void Initialize( Interface1^ i1 )
{
m_i1 = i1;
Class1^ c1 = safe_cast< Class1^ >( i1 );
c1->OnEvent += gcnew Class1::Eventhandler( this, &Class2::Notify );
m_pn2->Initialize( c1->GetNative() );
}
void Notify()
{
delete m_pn2;
m_pn2 = nullptr;
}
private:
Interface1^ m_i1;
CNative2* m_pn2;
};
}
精彩评论