繁体   English   中英

在引擎盖下引用弱引用的C#事件是一个好主意吗?

[英]Is it a good idea to implement a C# event with a weak reference under the hood?

我一直想知道是否值得使用类似下面的内容(粗略的概念证明代码)来实现弱事件(它们是合适的):

class Foo {

    private WeakEvent<EventArgs> _explodedEvent = new WeakEvent<EventArgs>();

    public event WeakEvent<EventArgs>.EventHandler Exploded {
        add { _explodedEvent += value; }
        remove { _explodedEvent -= value; }
    }

    private void OnExploded() {
        _explodedEvent.Invoke(this, EventArgs.Empty);
    }

    public void Explode() {
        OnExploded();
    }

}

允许其他类使用更常规的C#语法订阅和取消订阅事件,同时实际上使用弱引用实现:

static void Main(string[] args) {
    var foo = new Foo();
    foo.Exploded += (sender, e) => Console.WriteLine("Exploded!");

    foo.Explode();
    foo.Explode();
    foo.Explode();

    Console.ReadKey();
}

WeakEvent<TEventArgs>辅助类的定义如下:

public class WeakEvent<TEventArgs> where TEventArgs : EventArgs {

    public delegate void EventHandler(object sender, TEventArgs e);

    private List<WeakReference> _handlers = new List<WeakReference>();

    public void Invoke(object sender, TEventArgs e) {
        foreach (var handler in _handlers)
            ((EventHandler)handler.Target).Invoke(sender, e);
    }

    public static WeakEvent<TEventArgs> operator + (WeakEvent<TEventArgs> e, EventHandler handler) {
        e._handlers.Add(new WeakReference(handler));
        return e;
    }

    public static WeakEvent<TEventArgs> operator - (WeakEvent<TEventArgs> e, EventHandler handler) {
        e._handlers.RemoveAll(x => (EventHandler)x.Target == handler);
        return e;
    }

}

这是一个好方法吗? 这种方法有什么不良副作用吗?

这是一个坏主意,因为:

  1. 您的程序开始变得不确定,因为副作用取决于GC的操作。
  2. GCHandles的性能成本。

查看链接的答案。 这是95%的重复但不足以结束我认为的问题。 我会引用最相关的部分:


还存在由弱引用引起的语义差异非确定性 如果您将() => LaunchMissiles()挂钩到某个事件,您可能会发现有时会启动导弹。 其他时候GC已经带走了处理程序。 这可以通过引入另一个复杂程度的从属句柄来解决。

我个人觉得很少有事件的强烈参考性质是一个问题。 通常,事件连接在具有相同或非常相似的生命周期的对象之间。 例如,您可以在ASP.NET中的HTTP请求的上下文中挂接所有您想要的事件,因为在请求结束时, 所有内容都有资格进行收集。 任何泄漏的大小都是有限的,而且寿命很短。

关于您的特定实现的一些评论:

  1. 在调用它之前检查handler.Target的值是否为null ,这样就不会尝试使用已经处置的对象来执行它。

  2. C#具有关于如何使用事件的特殊访问规则。 除非该代码具有对事件的私有访问权限,否则您不能执行a.Event1 = a.Event2 + SomeOtherMethod 但是,这对于代表来说是允许的。 您的实现更像是委托而不是事件。 这可能不是一个主要的问题,但需要考虑的事情。

  3. 您的operator方法应返回一个新对象,而不是修改第一个参数并返回它。 实现operator +允许使用如下语法: a = b + c ,但在您的实现中,您正在修改b !的状态。 对于人们如何期望这些运营商的工作,这不是犹太人; 您需要返回一个新对象而不是修改现有对象。 (另外,因此你的实现不是线程安全的。从另一个线程调用operator +而另一个线程引发事件会引发异常,因为在foreach期间修改了集合。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM