我有一个为COM对象提供适配器的类。这是一个非常简化的版本:
public class DocumentWrapper
{
private COMDocument doc;
public DocumentWrapper(COMDocument doc)
{
this.doc = doc;
this.doc.OnClose += OnClose;
}
private void OnClose()
{
this.doc.OnClose -= OnClose;
this.doc = null;
}
public bool IsAlive { get { return this.doc != null; } }
}
问题是OnClose事件处理程序使两个对象都保持活动状态。两个物体应该具有相同的寿命,所以当一个物体消失时,另一个物体也应该存在,即没有人保持一个存活并且期望另一个物体消失。
我尝试过弱引用:
COMDocument com = CreateComDocument();
var doc = new DocWrapper(com);
WeakReference weak1 = new WeakReference(com);
WeakReference weak2 = new WeakReference(doc);
GC.Collect(2, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers();
GC.Collect(2, GCCollectionMode.Forced);
if (weak1.Target != null)
Console.WriteLine("COM was not collected");
if (weak2.Target != null)
Console.WriteLine("Wrapper was not collected");
如果未注册OnClose,则会收集这两个对象。如果注册了OnClose,则不会收集任何对象。如何在不丢失事件的情况下确保这对对象具有收藏价值?
答案 0 :(得分:1)
实际的解决方案要复杂得多,因为我不得不担心多个线程(终结器在后台线程上运行,并且取消注册COM事件必须调用COM线程,如果我不小心可能会触发死锁) 。但这里是如何构建事物的基本思想,以确保它可以收集垃圾:
public class DocumentWrapper
{
private COMDocument doc;
private WeakHandler closeHandler;
public DocumentWrapper(COMDocument doc)
{
this.doc = doc;
this.closeHandler = new WeakHandler(this);
}
~DocumentWrapper()
{
if (closeHandler != null)
closeHandler.Unregister();
}
public bool IsAlive { get { return this.doc != null; } }
private class WeakHandler : IDisposable
{
private WeakReference owner;
public WeakHander(DocumentWrapper owner)
{
this.owner = new WeakReference(owner);
owner.doc.OnClose += Unregister();
}
private void Unregister()
{
if (owner == null)
return;
var target = owner.Target as DocumentWrapper;
if (target != null && target.doc != null)
{
target.doc.OnClose -= OnClose;
target.closeHandler = null;
GC.SupressFinalize(target);
}
this.owner = null;
}
}
}