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 によって識別されるため、2 つの異なるイベントを同じ目的で使用することはできません (MouseUp、MouseDown、MouseMoved などを考えてください)。
  • メッセージはオブジェクトではなく静的なコンテキストに結合されているため、sayplayer1と notからのイベントの登録が難しくなっています。player2
  • また、メッセージはどこからでも呼び出すことができますが、イベントの呼び出しは常に、そのイベントを所有するクラス/オブジェクトに対してプライベートです。
于 2012-11-26T04:10:15.413 に答える
1

私は実際に非常によく似たパターンを使用して多くの成功を収めました。私が行ったもう 1 つの手順は、実際のメッセージ ハンドラーを 内にカプセル化して、MessageHandlerRegistryよりクリーンな構文を可能にすることでした。変更された例を次に示します。

Message.cs

public class Message
{

}

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

Program.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 に答える