4

所以这是我的代码:

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;
    }
}

Message只是其他类可以继承的空类。大多数Message派生都是具有相关属性的简单对象(这是针对游戏的,因此可能会有PlayerDamagedMessage伤害和攻击者作为属性。)

基本上,Player当他们受到攻击时,他们会在课堂上发布一个PlayerDamagedMessage,然后其他想要了解它的类可以订阅,然后在它发生时接收相关详细信息。

这适用于多条消息的原因是泛型类在 C# 中的工作方式。我的意思是,在幕后使用的每种不同的泛型类型都会有一个委托的副本。

实际上,我在玩耍时偶然发现了这一点,并且很兴奋,因为它确实简化了我的代码,并且在这一点上看起来几乎就像一种设计模式。

我在这里发帖询问使用这种方法的潜在缺点。幕后通用代表的数量是否有限制?这样的规模会有多好?

此外,有没有办法进行某种通用类型推断,以便语句不必这么长?

MessageHandler<MessagePlayerDamaged>.Publish(new MessagePlayerDamaged(this, this));

谢谢阅读。

4

4 回答 4

1

我在我的项目中使用了一个解决方案。

    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);
        }
    }

并测试示例:

    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; }
    }

你可以做MessageDispatcher一个单例。如果您的应用程序是多线程的,那么您需要考虑线程安全性。

于 2012-11-26T07:23:37.937 回答
1

与 C# 事件的主要区别:

  • 您的消息由它们的 Arguments-Class 标识,这使得不可能有两个不同的事件用于相同的目的(想想 MouseUp、MouseDown、MouseMoved 等)
  • 您的消息耦合到静态上下文,而不是对象,这使得很难从 sayplayer1和 not注册事件player2
  • 并且您的消息可以从任何地方调用,而事件调用对于拥有该事件的类/对象始终是私有的
于 2012-11-26T04:10:15.413 回答
1

我实际上使用了非常相似的模式并取得了很大的成功。我采取的进一步措施是将实际的消息处理程序封装在 aMessageHandlerRegistry中以允许更清晰的语法。这是您修改的示例:

消息.cs

public class Message
{

}

消息处理程序.cs

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);
    }
}

MessageHandlerRegistry.cs

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);
    }
}

程序.cs

class Program
{
    static void Main(string[] args)
    {
        MessageHandlerRegistry.Subscribe((Message m) => Console.WriteLine("Message received."));
        MessageHandlerRegistry.Publish(new Message());
    }
}

我看到的唯一缺点是过度松散耦合,在某些情况下使用传统的基于事件的方法更有意义,有时只发布消息更容易。

于 2012-11-26T04:01:17.007 回答
0

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

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;
    }
}

这样您就不必添加类型参数:

class IntMessage : Message
{
    public int Value = 100;
}

class StringMessage : Message
{
    public string Value = "a string";
}

static void Main(string[] args)
{
    MessageHandler.Subscribe((StringMessage m) => Console.WriteLine("String : " + m.Value));
    MessageHandler.Subscribe((StringMessage m) => Console.WriteLine("2nd String : " + m.Value));
    MessageHandler.Subscribe((IntMessage m) => Console.WriteLine("Int : " + m.Value));
    MessageHandler.Subscribe((IntMessage m) => Console.WriteLine("2nd Int : " + m.Value));

    MessageHandler.Publish(new IntMessage());
    MessageHandler.Publish(new StringMessage());
}
于 2013-03-14T15:03:06.430 回答