使用静态类/事件作为应用程序消息总线是否存在缺陷

时间:2010-05-13 19:30:32

标签: c# generics events

我有一个静态泛型类,它可以帮助我以极少的开销来移动事件:

public static class MessageBus<T> where T : EventArgs
{
    public static event EventHandler<T> MessageReceived;
    public static void SendMessage(object sender, T message)
    {
        if (MessageReceived != null)
            MessageReceived(sender, message);
    }
}

要创建系统范围的消息总线,我只需要定义一个EventArgs类来传递任意信息:

class MyEventArgs : EventArgs
{
    public string Message { get; set; }
}

我对这个事件感兴趣的任何地方,我只是连接一个处理程序:

MessageBus<MyEventArgs>.MessageReceived += (s,e) => DoSomething();

同样,触发事件同样容易:

MessageBus<MyEventArgs>.SendMessage(this, new MyEventArgs() {Message="hi mom"});

使用MessageBus和自定义EventArgs类,我可以为特定类型的消息提供应用程序范围的消息接收器。当您有多个表单时,例如显示客户信息以及可能更新该信息的几个表单,这会派上用场。这些形式都没有相互了解,也没有一个需要连接到静态的“超类”。

我有几个问题:

  1. fxCop抱怨使用静态方法和泛型,但这正是我在这里所追求的。我希望每个处理的消息类型都有一个MessageBus。使用带有泛型的静态可以使我免于编写维护MessageBus对象列表的所有代码。

  2. 是否通过MessageReceived事件将侦听对象保持为“活动”状态?

  3. 例如,也许我在Form.Load事件中有这个代码:

    MessageBus<CustomerChangedEventArgs>.MessageReceived += (s,e) => DoReload();
    

    当Form关闭时,Form是否保留在内存中,因为MessageReceived具有对其DoReload方法的引用?我应该在表单关闭时删除引用:

    MessageBus<CustomerChangedEventArgs>.MessageReceived -= (s,e) => DoReload();
    

3 个答案:

答案 0 :(得分:3)

嗯,是的,你应该,但如果你在你的例子中使用了lambda语法,我认为它不起作用(我的意思是,处理程序不会被成功取消注册)。 / p>

如果我错了,有人会纠正我,但我相信这是真的,因为使用lambda语法有效地创建了一个新的EventHandler<CustomerChangedEventArgs>对象,它在内存中有自己的位置。当您尝试删除此处理程序时,再次使用lambda语法,这将创建另一个EventHandler<CustomerChangedEventArgs>对象,该对象不等于您创建的第一个对象;所以第一个永远不会被取消注册。

可悲的是,我认为您需要实际定义这样的方法:

DoReload(object sender, CustomerChangedEventArgs e) {
    DoReload(); // your original overload, which doesn't actually care
                // about the sender and e parameters
}

这样你就可以:

MessageBus<CustomerChangedEventArgs>.MessageReceived += DoReload;

后来:

MessageBus<CustomerChangedEventArgs>.MessageReceived -= DoReload;

答案 1 :(得分:1)

是的,有问题。您的事件处理程序将导致表单对象保持引用,您必须显式取消注册事件处理程序。 lambdas使这变得不可能,你必须编写一个显式的处理程序。

此模式的名称为“Event Broker service”。它是由Microsoft的模式和实践团队发布的Composite UI Application Block的一部分。乞求,借用并窃取(如果不使用)你可以从中获得的东西。

答案 2 :(得分:0)

您可以使用弱引用来存储事件处理程序。这样,unhooked处理程序不会阻止对象的垃圾收集。

public static class MessageBus<T> where T : EventArgs
{
    private static List<WeakReference> _handlers = new List<WeakReference>();

    public static event EventHandler<T> MessageReceived
    {
        add
        {
            _handlers.Add(new WeakReference(value));
        }
        remove
        {
            // also remove "dead" (garbage collected) handlers
            _handlers.RemoveAll(wh => !wh.IsAlive  || wh.Target.Equals(value));
        }
    }

    public static void SendMessage(object sender, T message)
    {
        foreach(var weakHandler in _handlers)
        {
            if (weakHandler.IsAlive)
            {
                var handler = weakHandler.Target as EventHandler<T>;
                handler(sender, message);
            }
        }            
    }
}