我该如何让弱引用代理对象的行为像弱引用被代理对象的行为?

3
作为IPC框架的一部分,服务器端代码实现了整数到对象的映射(以便IPC消息可以使用整数引用对象)。
但是,该映射不直接存储到对象的引用,而是存储到IGUIObject实现,此接口由代理对象实现,这些代理对象包装实际对象。也就是说,有一个类似于下面的接口:
interface IGUIObject
{
    Rectangle Bounds { get; }
}

然后可能有一些实现方式,比如

class WindowsFormsControl : IGUIObject
{
    private Control m_control;

    public WindowsFormsControl( Control control )
    {
        m_control = control;
    }

    public virtual Rectangle Bounds
    {
        get
        {
            return m_control.Bounds;
        }
    }
}

映射类是一个单例,它的样子如下:

class ObjectDict
{
    private static readonly ObjectDict s_instance = new ObjectDict();
    private Dictionary<int, IGUIObject> m_idToObjectDict;
    private Dictionary<IGUIObject, int> m_objectToIdDict;
    private int m_lastObjectId;

    public static ObjectDict Instance
    {
        get
        {
            return s_instance;
        }
    }

    [MethodImpl(MethodImplOptions.Synchronized)]
    public IGUIObject objectForId( int id )
    {
        IGUIObject go;
        if ( m_idToObjectDict.TryGetValue( id, out go ) ) {
            return go;
        }
        return null;
    }

    [MethodImpl(MethodImplOptions.Synchronized)]
    public int idForObject( IGUIObject go )
    {
        // Lookup ID for objects, register object and return new ID if needed
    }

    // ...
}

问题在于,ObjectDict类不应该阻止应用程序对象被垃圾回收,即应该保持弱引用,在这种情况下,ObjectDict.objectForId应该抛出异常。
然而,
  • IGUIObject实现中使用弱引用似乎没有帮助,因为ObjectDict类是包装器对象的所有者。它们没有其他存储位置。只有在IPC系统接收到显式的“释放ID为12345的对象”消息时,才会从字典中删除包装器对象。我需要解决的问题是代理对象可能被垃圾回收,我想允许、检测和优雅地处理它。包装器对象应该与代理对象一样长寿(潜在地更长),但不能更短。

  • WindowsFormsExtension持有Control对象的弱引用,然后让ObjectCache持有IGUIObject实现的弱引用也没有帮助,因为代理对象(即WindowsFormsControl)并不反映所包含对象的“null-ness”。另一个复杂因素是,有许多(几十个)IGUIObject接口的实现,但只有一个ObjectDict类。因此,任何不需要我调整所有IGUIObject实现以开始使用弱引用的繁琐和容易出错的解决方案都更受欢迎。

有人知道如何调整代码,使ObjectDict不(间接)保持对IGUIObject实现包装的任何对象的强引用吗?

在IGUIObject实现中持有弱引用,为什么行不通?如果包装器无法访问,那么代理对象也无法访问。 - usr
@usr 我没有想到这一点,我很担心。ObjectDict 确实 是包装对象的所有者,即它不能持有对它们的弱引用。只要IPC系统没有显式发送“释放”消息将它们从字典中删除,包装对象就会一直存在。但是,代理对象可能已经消失了(它肯定应该被允许这样做!),我希望能够优雅地处理这种情况。 - Frerich Raabe
为什么不将字典定义为 Dictionary<int, WeakReference<IGUIObject>> 呢? - vgru
那么包装对象不应该使用弱引用来代理对象吗? - Lasse V. Karlsen
@LasseV.Karlsen 对,这听起来很像我在第二点中勾画出来的(并且放弃了)的情况。问题在于,我需要以某种方式传播代理对象消失的信息,并且有很多需要进行调整的 IGUIObject 实现。 - Frerich Raabe
显示剩余6条评论
1个回答

1
也许你可以再添加一层抽象 :)
class WeakGUIObject : IGUIObject
{
    WeakReference wr;

    public WindowsFormsControl( IGUIObject inner )
    {
        wr = new WR(inner);
    }

    public virtual Rectangle Bounds
    {
        get
        {
            var inner = wr.Target;
            if (inner == null) throw ObjectDisposedException();
            return inner.Bounds;
        }
    }
}

现在你可以进行字典引用并分发 WeakGUIObject 实例。这将自动使所有代理对象变为弱引用。
不确定整个设计是否是一个好主意。 ObjectDict 的客户端可能会发现他们的对象在随机时间消失并开始抛出异常。但这似乎是你的目标。

1
这是一个有趣的想法!确实,该设计意味着对象可以随时消失。然而,整个代码是调试工具的一部分,仅旨在“监视”应用程序,即它很容易承认事物可能会消失。主要目标是尽量不对被监视的应用程序产生太大影响。 - Frerich Raabe
唉,我不确定这个设计是否可行:使用这种设计,我们保留对代理对象的弱引用,也就是说,没有任何反对立即GC它们的东西。净效果将是,我们认为真正的对象已经被处理,即使它仍然活着:只是IGUIObject包装器被销毁了。也许如果有一种方法告诉GC将IGUIObject包装器和代理对象的生命周期链接起来,以便包装器对象与真实对象一样长寿(这也让我回到了问题标题)... - Frerich Raabe
您可以使用 ConditionalWeakTable 来链接对象生命周期。您可以向其添加一个项 (realObject, proxy)。这将使代理对象的生命周期与真实对象的生命周期完全相同。 - usr
确实如此,但不幸的是,我现在还需要支持.NET 3。我认为,如果我能找到一种简单的方法来从包装对象(例如某些“控件”)建立“反向引用”到包装器(例如“WindowsFormsControl”),那就太完美了:两者在生命周期方面将被耦合。 - Frerich Raabe
也许如果我只是设置一个事件监听器(实际上什么也不做),这样Control对象就需要持有对包装器的引用。 - Frerich Raabe
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接