WeakEventDelegate实现 - 反馈请求

时间:2011-07-04 16:57:27

标签: c# .net delegates garbage-collection

我刚刚在.NET中实现了WeakEventDelegate类。

我在http://code.logos.com/blog/2008/08/event_subscription_using_weak_references.htmlhttp://blogs.msdn.com/b/greg_schechter/archive/2004/05/27/143605.aspx

中看到了其他文章实现此类内容的效果

然而,我所实现的实现并不那么复杂(虽然不够灵活),并且似乎做了这个工作,所以我想知道是否有一些我错过的东西。

以下实施是否存在任何问题,但相对缺乏灵活性?

public class WeakEventDelegate<TEventArgs> 
    where TEventArgs : EventArgs
{
    private readonly WeakReference handlerReference;

    public WeakEventDelegate(Action<object, TEventArgs> handler)
    {
        handlerReference = new WeakReference(handler);
    }

    public void Handle(object source, TEventArgs e)
    {
        Action<object, TEventArgs> unwrappedHandler = (Action<object, TEventArgs>)handlerReference.Target;
        if (unwrappedHandler != null)
        {
            unwrappedHandler.Invoke(source, e);
        }
    }
}

编辑:我写这个类的唯一目的是阻止发布者向代理人的隐式引用,以防止垃圾收集。

意思是,而不是写作:

void subscribe()
{
    publisher.RaiseCustomEvent += this.HandleCustomEvent;
}

我会写:

private readonly WeakDelegate<CustomEventArgs> _customHandler = new WeakDelegate<CustomEventArgs>(this.HandleCustomEvent);
void subscribe()
{
    publisher.RaiseCustomEvent += _customHandler.Handle;
}

我想到的那个类的主要用例是针对一些具有我几乎无法控制的生命周期的集合类(订阅者)。 (但是其中一种情况发生在WPF数据绑定中,因此它将成为使用推荐的弱事件基础结构的完美候选者。)

4 个答案:

答案 0 :(得分:3)

这里的主要问题是订阅您的代理人(handlerReference.Target)的任何内容都会使handlerReference保持活着。

包装它可以为您提供在不保留对它的引用的情况下调用您的代理的方法,但不会阻止订阅者保持引用活动。

框架功能通过拥有中介来支持Weak Event Patterns。订阅通过中间人处理,等式的双方通过弱引用维护。没有任何东西可以直接引用委托 - 因为对委托的引用可以使对象保持活动状态。

答案 1 :(得分:0)

我发现了该实现的一个关键问题 - 它只有在我保留对其他地方的委托(在构造函数中传递的处理程序)的引用时才有效。否则,将收集该委托对象,并且该事件永远不会再次触发。

答案 2 :(得分:0)

思想

我喜欢在处理事件的类一侧实现弱委托的想法,因为我不想仅在某些情况下使用弱引用。对于我个人的ui框架,我有时会对事件作出反应而不保留对原始对象的引用:

void HandlePropertyChanged(Object sender, PropertyChangedArgs args)
{
    // Do some stuff with the sender
}

这需要非弱代表,而其他对象需要弱代理。

解决方案

我偶然发现了你所遇到的相同问题,并且没有使用WeakReference代理,而是使用WeakReference作为委托的目标并复制空委托。

// Create a weak reference to the target
_Sender = new WeakReference(target.Target);
_CallDelegate = (Action<TThis, TSender, TArgs>)Delegate.CreateDelegate(CallDelegateType, target.Method);

这很有效,但仍然存在一个大问题:如何在不再使用时删除该事件?

那么也有一个解决方案,但解决方案需要一些反射代码并且是错误的来源:

_RemoveMethod = (Action<DataEventHandler<TSender, TArgs>>)Delegate.CreateDelegate(AddMethodType, eventHolder, eventDeclaration.GetRemoveMethod());

eventDeclaration是一个EventInfo实例,带有构造函数的参数,为用户创建第二个只接受实例的构造函数({{1} })和一个表示事件名称的字符串。

当对象不再存在时,eventHolder - 委托被调用并移除_RemoveMethod

用法

WeakDelegate

正如您所看到的还有一个问题:n.Disposing += (_DisposeDelegate = new WeakDelegate<Drawable, IComponent,Object>(n, "Disposing", HandleRootDisposing)); 必须保留在内存中,以便稍后删除该事件。这也有一个解决方案,但它需要覆盖WeakDelegate的相等方法,并将所有委托存储在哈希表中,这可能会大大减慢应用程序的速度。

答案 3 :(得分:0)

弱事件的一个主要问题是,如果订阅列表仅包含对订阅者的引用,则事件发布者无法知道是否有任何关于该事件的内容。从根本上说,只要对事件处理程序修改的任何内容存在任何引用,事件就应该保持活动状态,但事件发布者无法知道这些引用是否存在或者它们可能采用的形式。

另一种方法是让每个事件订阅者传递对实现合适的事件通知接口的对象的引用,并使该接口包括指示订阅者是否仍然对该事件感兴趣的属性。添加事件订阅时,事件发布者有时会轮询订阅者以查看他们是否仍然感兴趣;这些操作可以方便地进行零碎或批量处理,但每个订阅的平均订阅者轮询操作数应该以常数为界。

请注意,现有的事件基础结构在弱引用方面的效果相当差,因为重复添加和放弃不会触发的事件的订阅者可能会因持有WeakReferences的对象而创建一个任意大的订阅列表,而不会给任何这些物品一个清理自己的机会。