试图设计一个小的消息处理程序类来模拟C#事件。这种方法的缺点是什么?

试图设计一个小的消息处理程序类来模拟C#事件。这种方法的缺点是什么?,c#,generics,delegates,messaging,C#,Generics,Delegates,Messaging,这是我的代码: static class MessageHandler<T> where T : Message { public delegate void MessageDelegate(T m); private static MessageDelegate messageHandlers; public static void Publish(T message) { messageHandlers(message);

这是我的代码:

static class MessageHandler<T> where T : Message
{
    public delegate void MessageDelegate(T m);

    private static MessageDelegate messageHandlers;

    public static void Publish(T message)
    {
        messageHandlers(message);
    }

    public static void Subscribe(MessageDelegate messageHandler)
    {
        messageHandlers += messageHandler;
    }
}
静态类MessageHandler,其中T:Message
{
公共委托无效消息委托(TM);
私有静态MessageDelegate messageHandlers;
公共静态无效发布(T消息)
{
messageHandlers(message);
}
公共静态无效订阅(MessageDelegate messageHandler)
{
messageHandlers+=messageHandler;
}
}
Message
只是一个空类,其他类可以从中继承。大多数
消息
派生是具有相关属性的简单对象(这是针对游戏的,因此可能会有
玩家伤害消息
,属性为伤害和攻击者。)

基本上,在
Player
类中,当他们受到攻击时,他们会发布
PlayerDamagedMessage
,然后其他想要知道的类可以订阅,然后在发生攻击时接收相关细节

这可以处理多条消息的原因是C#中泛型类的工作方式。我的意思是,对于封面下使用的每个不同的泛型类型,都会有一份委托的副本

实际上,我在玩游戏时意外地发现了这一点,我很兴奋,因为它简化了我的代码,现在看起来几乎像一个设计模式

我在这里发帖是想问一下使用这种方法的潜在缺点。封面下的通用代理数量是否有限制?像这样的秤有多好

还有,有没有办法进行某种泛型类型推断,这样语句就不必这么长

MessageHandler.Publish(newmessageplayerdamaged(this,this))


感谢阅读。

实际上,我使用了一种非常相似的模式,并取得了很大成功。我采取的另一个步骤是将实际的消息处理程序封装在
MessageHandlerRegistry
中,以允许更清晰的语法。以下是您的示例:

Message.cs

public class Message
{

}
public class MessageHandler<T> where T : Message
{
    private Action<T> messageHandlers;

    public void Publish(T message)
    {
        messageHandlers(message);
    }

    public void Subscribe(Action<T> messageHandler)
    {
        messageHandlers = (Action<T>) Delegate.Combine(messageHandlers, messageHandler);
    }
}
public static class MessageHandlerRegistry
{
    private static readonly IDictionary<Type, object> _handlers = new Dictionary<Type, object>();

    public static void Publish<T>(T m) where T : Message
    {
        if (_handlers.ContainsKey(typeof (T)))
        {
            ((MessageHandler<T>) _handlers[typeof (T)]).Publish(m);
        }
    }

    public static void Subscribe<T>(Action<T> messageHandler) where T : Message
    {
        if (!_handlers.ContainsKey(typeof (T)))
        {
            _handlers[typeof (T)] = new MessageHandler<T>();
        }
        ((MessageHandler<T>) _handlers[typeof (T)]).Subscribe(messageHandler);
    }
}
class Program
{
    static void Main(string[] args)
    {
        MessageHandlerRegistry.Subscribe((Message m) => Console.WriteLine("Message received."));
        MessageHandlerRegistry.Publish(new Message());
    }
}
MessageHandler.cs

public class Message
{

}
public class MessageHandler<T> where T : Message
{
    private Action<T> messageHandlers;

    public void Publish(T message)
    {
        messageHandlers(message);
    }

    public void Subscribe(Action<T> messageHandler)
    {
        messageHandlers = (Action<T>) Delegate.Combine(messageHandlers, messageHandler);
    }
}
public static class MessageHandlerRegistry
{
    private static readonly IDictionary<Type, object> _handlers = new Dictionary<Type, object>();

    public static void Publish<T>(T m) where T : Message
    {
        if (_handlers.ContainsKey(typeof (T)))
        {
            ((MessageHandler<T>) _handlers[typeof (T)]).Publish(m);
        }
    }

    public static void Subscribe<T>(Action<T> messageHandler) where T : Message
    {
        if (!_handlers.ContainsKey(typeof (T)))
        {
            _handlers[typeof (T)] = new MessageHandler<T>();
        }
        ((MessageHandler<T>) _handlers[typeof (T)]).Subscribe(messageHandler);
    }
}
class Program
{
    static void Main(string[] args)
    {
        MessageHandlerRegistry.Subscribe((Message m) => Console.WriteLine("Message received."));
        MessageHandlerRegistry.Publish(new Message());
    }
}
我所看到的唯一的缺点是过于松散耦合,在某些情况下,使用传统的基于事件的方法更有意义,有时只发布消息更容易

与C#事件的主要区别:

  • 您的消息由它们的Arguments类标识,这使得不可能有两个不同的事件用于相同的目的(想想MouseUp、MouseDown、MouseMoved等)
  • 您的消息耦合到一个静态上下文,而不是对象,这使得很难从say
    player1
    和not
    player2
    注册事件
  • 您的消息可以从任何地方调用,而事件调用始终是拥有该事件的类/对象的私有

    • 我在项目中使用了一种解决方案

          public class MessageDispatcher {
              private readonly Dictionary<Type, MulticastDelegate> registeredHandlers = new Dictionary<Type, MulticastDelegate>();
      
              private delegate void MessageActionDelegate<in T>(T message);
      
              public void Register<T>(Action<T> action) {
                  Type messageType = typeof (T);
                  if (registeredHandlers.ContainsKey(messageType)) {
                      var messageDelegate = (MessageActionDelegate<T>) registeredHandlers[messageType];
                      registeredHandlers[messageType] = messageDelegate + new MessageActionDelegate<T>(action);
                  }
                  else {
                      registeredHandlers.Add(messageType, new MessageActionDelegate<T>(action));
                  }
      
              }
      
              public void Deregister<T>() {
                  Type messageType = typeof (T);
                  if (registeredHandlers.ContainsKey(messageType)) {
                      registeredHandlers.Remove(messageType);
                  }
              }
      
              public void DeregisterAll() {
                  registeredHandlers.Clear();
              }
      
              public void Send<T>(T message) {
                  Type messageType = typeof (T);
                  if (!registeredHandlers.ContainsKey(messageType)) return;
      
                  ((MessageActionDelegate<T>) registeredHandlers[messageType])(message);
              }
          }
      
      公共类MessageDispatcher{
      专用只读词典注册表项Handlers=new Dictionary();
      私有委托无效消息ActionDelegate(T消息);
      公共无效登记册(行动){
      类型messageType=typeof(T);
      if(registeredHandlers.ContainsKey(messageType)){
      var messageDelegate=(MessageActionDelegate)registeredHandler[messageType];
      registeredHandlers[messageType]=messageDelegate+新MessageActionDelegate(操作);
      }
      否则{
      Add(messageType,newmessageactiondelegate(action));
      }
      }
      公开作废注销人(){
      类型messageType=typeof(T);
      if(registeredHandlers.ContainsKey(messageType)){
      registeredHandlers.Remove(messageType);
      }
      }
      公开作废注销所有(){
      registeredHandlers.Clear();
      }
      公共无效发送(T消息){
      类型messageType=typeof(T);
      if(!registeredHandlers.ContainsKey(messageType))返回;
      ((MessageActionDelegate)注册句柄[messageType])(消息);
      }
      }
      
      和测试示例:

          private static void Main(string[] args) {
              var messenger = new MessageDispatcher();
              messenger.Register<Message>(m => Console.WriteLine(m.Text));
              messenger.Send(new Message() { Text = "Good morning, sir."});
              messenger.Register<Message>(m => Console.WriteLine(m.Text + " It's nice weather today."));
              messenger.Register<Notification>(n => Console.WriteLine(n.Text));
              messenger.Send(new Message() { Text = "How do you feel? "});
              messenger.Send(new Notification() { Text = "Cup of tea, sir?" });
              messenger.Deregister<Message>();
              messenger.Send(new Message() { Text = "Good bye" });
              Console.ReadLine();
          }
      
          public class Message {
              public string Text { get; set; }
          }
      
          public class Notification {
              public string Text { get; set; }
          }
      
      private static void Main(字符串[]args){
      var messenger=new MessageDispatcher();
      messenger.Register(m=>Console.WriteLine(m.Text));
      Send(newmessage(){Text=“早上好,先生。”});
      messenger.Register(m=>Console.WriteLine(m.Text+“今天天气不错”);
      messenger.Register(n=>Console.WriteLine(n.Text));
      Send(newmessage(){Text=“感觉如何?”});
      Send(新通知(){Text=“一杯茶,先生?”});
      messenger.Deregister();
      Send(新消息(){Text=“再见”});
      Console.ReadLine();
      }
      公共类消息{
      公共字符串文本{get;set;}
      }
      公开课通知{
      公共字符串文本{get;set;}
      }
      
      您可以将
      MessageDispatcher
      设置为单例。
      如果您的应用程序是多线程的,那么您需要考虑线程安全。

      我找到了一种使用泛型的方法,我添加了一个私有注册表类来存储静态委托:

      class Message
      {
      }
      
      class MessageHandler
      {
          public static void Publish<T>(T message) where T : Message
          {
              Registry<T>.action(message);
          }
      
          public static void Subscribe<T>(Action<T> h) where T : Message
          {
              Registry<T>.action += h;
          }
      
          private class Registry<T> where T : Message
          {
              public static Action<T> action;
          }
      }
      

      隐马尔可夫模型。。我喜欢一般的概念,谢谢你的想法。我遇到的问题是,
      Subscribe
      无法推断类型。此外,Registry类还使用字典、对象、类型检查等。所有我最初试图摆脱的东西。@RyanPeschel我同意。我更新了我的答案,以便能够推断出类型。应该能让你更靠近一点。