【问题标题】:Event Bus implementation question事件总线实现问题
【发布时间】:2011-07-09 11:10:26
【问题描述】:

我正在尝试实现简单的事件总线

我是这样开始的:

public class RegistrationData
{
    public object RegisteredObject { get; set; }
    public Type eventType { get; set; }
    public EventHandler MethodToInvoke;
}
public class EventBus
{
    private static EventBus instance;
    private static readonly object lockObject = new object();
    private static List<RegistrationData> registrationList;

    private EventBus()
    {
    }

    public static EventBus Instance
    {
        get
        {
            lock (lockObject)
            {
                registrationList = new List<RegistrationData>();
                return instance ?? new EventBus();
            }
        }
    }

    public void Subscribe(RegistrationData registrationData)
    {
        if(!registrationList.Contains(registrationData)) registrationList.Add(registrationData);
    }

    public void Unsubscribe(object objectToUnregister, Type eventType)
    {
        foreach(RegistrationData data in registrationList)
            if (data.RegisteredObject == objectToUnregister && data.eventType == eventType) registrationList.Remove(data);
    }

    public void UnregisterAllMessages(object objectToUnregister)
    {
        foreach(RegistrationData data in registrationList)
            if(data.RegisteredObject == objectToUnregister) registrationList.Remove(data);
    }

    public void PublishEvent(object sender, EventArgs eventArgs)
    {
        foreach (RegistrationData data in registrationList)
            if (EventArgs is typeof(data.Type)) data.MethodToInvoke(sender, eventArgs);
    }
}

但我在 PublishEvent 方法中有问题。我无法确定事件参数的类型。 而且我怀疑这一切都是错误的。

有人可以指出我做错了什么,我应该如何实施? 或者事件总线通常是如何实现的,或者我可以使用一些框架来代替自己实现并花时间在上面。

【问题讨论】:

标签: c# .net event-bus


【解决方案1】:

嗯,不太确定您的 Eventbus 应该如何运行。如果您不知道自己的目标是什么,那么看看其他人是如何解决这个问题的可能会很有用。

  • 有一个接地气的event aggregator in the caliburn.micro项目
  • 当我需要一个事件聚合器时,我喜欢使用MemBus,部分原因是我自己编写了它,部分原因是它满足了我在这方面的所有需求。它比 caliburn 的涉及更多,但它具有更多功能

【讨论】:

  • caliburn.micro 事件聚合器看起来不错。我可能会用它。明天我会看看 MemBus,看看我会使用哪个。
  • MemBus 似乎没有任何文档,开始时非常不愉快。你知道我在哪里可以找到有关 MemBus 的文档吗?
  • 您可以阅读我的博客文章realfiction.net/content/tag/membus 以及代码存储位置的介绍:github.com/flq/MemBus/wiki
  • 链接到您的博客不起作用,我认为这是正确的链接? realfiction.net/tag/membus/0
  • 为我工作...网站可能已关闭
【解决方案2】:

我认为您应该从定义事件总线开始。您认为事件总线与用于触发和接收事件的内置 .NET 机制之间的区别是什么?到目前为止,您所拥有的看起来并不比 .NET 事件的等价物更多。 .NET 本质上支持事件处理,因此如果您需要的不仅仅是 .NET 已经提供的更多内容,您就不需要事件总线:

class Program
{
  static void Main(string[] args)
  {
     BusinessObject1 bo = new BusinessObject1("First Value");
     // Subscribe
     bo.Publish += new BusinessObject.PublishObject(bo_Publish);
     bo.Update("Second Value");
     // UnSubscribe
     bo.Publish -= new BusinessObject.PublishObject(bo_Publish);
     bo.Update("Third Value");
     // Subscribe multiple
     bo.Publish += new BusinessObject.PublishObject(bo_Publish);
     bo.Publish += new BusinessObject.PublishObject(bo_Publish2);
     bo.Update("Fourth Value");
     // UnregisterAllMessages
     bo.UnsubcribeAll();
     bo.Update("Fifth Value");
  }

  static void bo_Publish(BusinessObject sender, EventArgs args)
  {
     if (sender is BusinessObject1)
     {
        BusinessObject1 bo1 = (BusinessObject1)sender;
        BusinessObject1.PublishBusinessObject1EventArgs args1 =
           (BusinessObject1.PublishBusinessObject1EventArgs)args;
        Console.WriteLine("Updated {0} to {1}", args1.oldValue, bo1.Value);
     }
  }

  static void bo_Publish2(BusinessObject sender, EventArgs args)
  {
     if (sender is BusinessObject1)
     {
        BusinessObject1 bo1 = (BusinessObject1)sender;
        BusinessObject1.PublishBusinessObject1EventArgs args1 =
           (BusinessObject1.PublishBusinessObject1EventArgs)args;
        Console.WriteLine("Second handler detected updated of {0} to {1}", args1.oldValue, bo1.Value);
     }
  }
}

abstract class BusinessObject
{
  public delegate void PublishObject(BusinessObject sender, EventArgs args);
  public event PublishObject Publish;
  // PublishEvent
  protected void Update(EventArgs args)
  {
     if (Publish != null)
        Publish(this, args);
  }
  public void UnsubcribeAll()
  {
     Publish = null;
  }
}

class BusinessObject1 : BusinessObject
{
  public class PublishBusinessObject1EventArgs : EventArgs
  {
     public string oldValue;
     public PublishBusinessObject1EventArgs(string oldValue)
     {
        this.oldValue = oldValue;
     }
  }

  public delegate void PublishBusinessObject1(BusinessObject1 sender, PublishBusinessObject1EventArgs args);

  public string Value {get; private set;}

  public BusinessObject1(string value)
  {
     this.Value = value;
  }

  public void Update(string newValue)
  {
     PublishBusinessObject1EventArgs args = new PublishBusinessObject1EventArgs(Value);
     Value = newValue;
     base.Update(args);
  }
}

编辑: 如果您不希望您的业务对象必须从公共基类继承(正如您在评论中建议的那样),您可以进行一些修改,以便 EventBus 更加独立,但您仍然不需要重新实现执行此操作的所有事件注册框架:

class Program
{
  static void Main(string[] args)
  {
     BusinessObject1 bo = new BusinessObject1("First Value");
     // Subscribe
     EventBus.Publish += new EventBus.PublishObject(EventBus_Publish);
     bo.Update("Second Value");
     // UnSubscribe
     EventBus.Publish -= new EventBus.PublishObject(EventBus_Publish);
     bo.Update("Third Value");
     // Subscribe multiple
     EventBus.Publish += new EventBus.PublishObject(EventBus_Publish);
     EventBus.Publish += new EventBus.PublishObject(EventBus_Publish2);
     bo.Update("Fourth Value");
     // UnregisterAllMessages
     EventBus.UnsubcribeAll();
     bo.Update("Fifth Value");
  }

  static void EventBus_Publish(object sender, EventArgs args)
  {
     if (sender is BusinessObject1)
     {
        BusinessObject1 bo1 = (BusinessObject1)sender;
        BusinessObject1.PublishBusinessObject1EventArgs args1 =
           (BusinessObject1.PublishBusinessObject1EventArgs)args;
        Console.WriteLine("Updated {0} to {1}", args1.oldValue, bo1.Value);
     }
  }

  static void EventBus_Publish2(object sender, EventArgs args)
  {
     if (sender is BusinessObject1)
     {
        BusinessObject1 bo1 = (BusinessObject1)sender;
        BusinessObject1.PublishBusinessObject1EventArgs args1 =
           (BusinessObject1.PublishBusinessObject1EventArgs)args;
        Console.WriteLine("Second handler detected updated of {0} to {1}", args1.oldValue, bo1.Value);
     }
  }
}

static class EventBus
{
  public delegate void PublishObject(object sender, EventArgs args);
  public static event PublishObject Publish;
  // PublishEvent
  public static void Update(object sender, EventArgs args)
  {
     if (Publish != null)
        Publish(sender, args);
  }
  public static void UnsubcribeAll()
  {
     Publish = null;
  }
}

class BusinessObject1
{
  public class PublishBusinessObject1EventArgs : EventArgs
  {
     public string oldValue;
     public PublishBusinessObject1EventArgs(string oldValue)
     {
        this.oldValue = oldValue;
     }
  }

  public delegate void PublishBusinessObject1(BusinessObject1 sender, PublishBusinessObject1EventArgs args);

  public string Value { get; private set; }

  public BusinessObject1(string value)
  {
     this.Value = value;
  }

  public void Update(string newValue)
  {
     PublishBusinessObject1EventArgs args = new PublishBusinessObject1EventArgs(Value);
     Value = newValue;
     EventBus.Update(this, args);
  }
}

编辑 2: 顺便说一句,如果您想对订阅过程进行更多控制,您也可以通过定义自定义事件访问器来获得更多控制,如 http://msdn.microsoft.com/en-us/library/bb882534.aspx 所述:

static class EventBus
{
  public delegate void PublishObject(object sender, EventArgs args);
  private static List<PublishObject> subscribers = new List<PublishObject>();
  public static event PublishObject Publish
  {
     add
     {
        subscribers.Add(value);
        Console.WriteLine("Added subscriber {0}.{1}", value.Method.DeclaringType.Name, value.Method.Name);
     }
     remove
     {
        bool result = subscribers.Remove(value);
        Console.WriteLine("Removed subscriber {0}.{1} ({2})", value.Method.DeclaringType.Name, value.Method.Name, result ? "success" : "failure");
     }
  }
  // PublishEvent
  public static void Update(object sender, EventArgs args)
  {
     foreach (PublishObject p in subscribers)
     {
        Console.WriteLine("Publishing to {0}.{1}", p.Method.DeclaringType.Name, p.Method.Name);
        p.Invoke(sender, args);
     }
  }
  public static void UnsubcribeAll()
  {
     subscribers.Clear();
  }
}

【讨论】:

  • 好吧,这样当对象想要发布事件时,它需要定义委托和 UnsubscribeAll() 方法。在我的情况下,任何类的对象都可以发布事件,只需调用 EventBus 实例而不是传递事件,例如: eventBus.PublishEvent(this, stringEvent);在您的情况下,我将需要我的所有对象使用这些定义的方法继承一些基类。如果我错了,请纠正我。
  • 通过一些更改(如上所示),您可以消除继承,并保持几乎相同的通用实现。
  • 这有点好。但同样,在我的例子中,我有更多的灵活性。因为我只需要为每个事件监听器实现 IListener 接口。并初始化将在事件触发时调用的方法。以您的方式,我需要知道我的组件将从哪些组件接收事件,以及注册到每个组件。在我的情况下,我只需要注册特定的事件类型,并将接收来自任何触发该类型事件的组件的所有事件。我想出了如何解决这个问题。但是非常感谢,您帮助我更好地理解问题。 :)
【解决方案3】:

嗯,作为第一个建议,在我看来,您好像是在尝试将其实现为单例。不然怎么办

private static EventBus instance;

适合吗?但是私有实例成员永远不会分配到任何地方,这是我建议你应该解决的一件事。作为参考,here's a really good article 关于单例的各种实现。如果您可以访问 .net4,我建议您使用 LazySingleton3 方法。

唯一想到的另一件事是,这看起来可能是 Generics 的用例。看看EventHandler&lt;TEventArgs&gt; Delegate

除此之外,我不能推荐更多,因为我不完全了解您要做什么。

编辑

this question 上查看the accepted answer。它包含指向几年前实现此功能的人的blog post 的链接。看来您不需要重新发明轮子。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-23
    • 1970-01-01
    • 1970-01-01
    • 2018-09-23
    相关资源
    最近更新 更多