Вопрос реализации Event Bus - PullRequest
2 голосов
/ 09 июля 2011

Я пытаюсь реализовать простую шину событий

Я начал так:

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. Я не могу определить тип аргумента события. И я подозреваю, что все это довольно неправильно.

Может кто-нибудь указать, что я делаю неправильно, как мне это реализовать? Или как вообще реализована шина событий, или какая-то инфраструктура, которую я могу использовать вместо реализации своей собственной и тратить на это время.

Ответы [ 3 ]

2 голосов
/ 09 июля 2011

Хм, не совсем уверен, как должен вести себя ваш Eventbus. Если не знать, куда вы направляетесь, может быть полезно посмотреть, как другие люди реализовали проблему.

  • В проекте caliburn.micro
  • Мне нравится использовать MemBus , когда мне нужен агрегатор событий, частично потому, что я написал его сам, частично потому, что он покрывает все мои потребности в этом отношении. Он более сложный, чем у калибра, но тогда у него больше возможностей
2 голосов
/ 09 июля 2011

Я думаю, вы должны начать с определения Event Bus. В чем вы видите разницу между шиной событий и встроенными механизмами .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);
  }
}

Edit: Если вы не хотите, чтобы ваши бизнес-объекты наследовали от общего базового класса (как вы предложили в своем комментарии), вы можете внести несколько изменений, чтобы 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();
  }
}
1 голос
/ 09 июля 2011

Ну, как первое предположение, мне кажется, что вы пытаетесь реализовать это как синглтон. В противном случае, что бы

private static EventBus instance;

хорошо для? Но член частного экземпляра никогда не назначается нигде, это одна вещь, которую я бы посоветовал вам исправить. Для справки вот действительно хорошая статья о различных реализациях синглетонов. Если у вас есть доступ к .net4, я бы посоветовал вам использовать LazySingleton3 подход.

Единственное, что приходит на ум, это то, что это может быть сценарий использования для Generics . Посмотрите на EventHandler<TEventArgs> Delegate.

Кроме того, я не могу рекомендовать намного больше, поскольку я не совсем понимаю, что вы пытаетесь сделать.

EDIT

Посмотрите принятый ответ на этот вопрос . Он содержит ссылку на сообщение в блоге того, кто реализовал это несколько лет назад. Похоже, вам не нужно заново изобретать колесо.

...