3

これが以前に尋ねられたかどうかはわかりませんが、これが何なのか、私がやろうとしていることは正確に何と呼ばれているのか正確にはわからないので、それを探す方法が本当にわかりませんでした...

Unity3D で使用するデリゲートベースのメッセージング汎用システムを持っています - hereから取得しました。

UAクロスリンク

次のように使用されます。

 // Writing an event listener

    void OnSpeedChanged(float speed)
    {
        this.speed = speed;
    }

// Registering an event listener

    void OnEnable()
    {
        Messenger<float>.AddListener("speed changed", OnSpeedChanged);
    }

// Unregistering an event listener

    void OnDisable()
    {
        Messenger<float>.RemoveListener("speed changed", OnSpeedChanged);
    }

私が抱えている問題は、コードが現在非常に乾燥していないことです(コピーペーストがたくさんあります)。うまくいけばパラメータ化して、より一般的なものにすることで、コードを乾燥させたいと考えています。

関連するコードを投稿します - 回答するために、コードを詳細に理解する必要はなく、コードが何をしているのかを理解する必要はありません。

舞台裏で何かをするクラスは次のとおりです。

static internal class MessengerInternal
{
    static public Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();
    static public readonly MessengerMode DEFAULT_MODE = MessengerMode.REQUIRE_LISTENER;

    static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded)
    {
        if (!eventTable.ContainsKey(eventType)) {
            eventTable.Add(eventType, null);
        }

        Delegate d = eventTable[eventType];
        if (d != null && d.GetType() != listenerBeingAdded.GetType()) {
            throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));
        }
    }

    static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved)
    {
        if (eventTable.ContainsKey(eventType)) {
            Delegate d = eventTable[eventType];

            if (d == null) {
                throw new ListenerException(string.Format("Attempting to remove listener with for event type {0} but current listener is null.", eventType));
            }
            else if (d.GetType() != listenerBeingRemoved.GetType()) {
                throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
            }
        }
        else {
            throw new ListenerException(string.Format("Attempting to remove listener for type {0} but Messenger doesn't know about this event type.", eventType));
        }
    }

    static public void OnListenerRemoved(string eventType)
    {
        if (eventTable[eventType] == null) {
            eventTable.Remove(eventType);
        }
    }

    static public void OnBroadcasting(string eventType, MessengerMode mode)
    {
        if (mode == MessengerMode.REQUIRE_LISTENER && !eventTable.ContainsKey(eventType)) {
            throw new BroadcastException(string.Format("Broadcasting message {0} but no listener found.", eventType));
        }
    }
}

これで、引数が 1 つ、2 つ、3 つ、またはまったくない汎用メッセンジャー クラスができました。したがって、ユーザーは適切なイベント ハンドラーを選択してイベントをサブスクライブできます。

一般的な引数を取らないバージョンは次のとおりです。

// No parameters
static public class Messenger {
    private static Dictionary<string, Delegate> eventTable = MessengerInternal.eventTable;

    static public void AddListener(string eventType, Callback handler) {
        MessengerInternal.OnListenerAdding(eventType, handler);
        eventTable[eventType] = (Callback)eventTable[eventType] + handler;
    }

    static public void RemoveListener(string eventType, Callback handler) {
        MessengerInternal.OnListenerRemoving(eventType, handler);   
        eventTable[eventType] = (Callback)eventTable[eventType] - handler;
        MessengerInternal.OnListenerRemoved(eventType);
    }

    static public void Broadcast(string eventType) {
        Broadcast(eventType, MessengerInternal.DEFAULT_MODE);
    }

    static public void Broadcast(string eventType, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        Delegate d;
        if (eventTable.TryGetValue(eventType, out d)) {
            Callback callback = d as Callback;
            if (callback != null) {
                callback();
            } else {
                throw MessengerInternal.CreateBroadcastSignatureException(eventType);
            }
        }
    }
}

引数を 1 つ取るバージョンを次に示します (コピーして貼り付け、T を追加するだけです)。

// One parameter
static public class Messenger<T> {
    private static Dictionary<string, Delegate> eventTable = MessengerInternal.eventTable;

    static public void AddListener(string eventType, Callback<T> handler) {
        MessengerInternal.OnListenerAdding(eventType, handler);
        eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;
    }

    static public void RemoveListener(string eventType, Callback<T> handler) {
        MessengerInternal.OnListenerRemoving(eventType, handler);
        eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;
        MessengerInternal.OnListenerRemoved(eventType);
    }

    static public void Broadcast(string eventType, T arg1) {
        Broadcast(eventType, arg1, MessengerInternal.DEFAULT_MODE);
    }

    static public void Broadcast(string eventType, T arg1, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        Delegate d;
        if (eventTable.TryGetValue(eventType, out d)) {
            Callback<T> callback = d as Callback<T>;
            if (callback != null) {
                callback(arg1);
            } else {
                throw MessengerInternal.CreateBroadcastSignatureException(eventType);
            }
        }
    }
}

すでにお察しのとおり、2 つの引数を取るものは、もう一度コピーして貼り付け、その他のジェネリック型を追加するだけです<T, U>

これは私が排除しようとしている部分ですが、まだ方法がわかりません。より正確には、私が探しているのは次のとおりです。Messengerクラスは1つだけですが、次のことができます。

Messenger<float>.Subscribe("player dead", OnDead);
Messenger<int, bool>.Subscribe("on something", OnSomething);
Messenger<bool, float, MyType>.Subscribe( stuff );

または、(どちらでもかまいません)

Messenger.Subscribe<float> ("player dead", OnDead);

あなたはアイデアを得ました...

どうすればそれを行うことができますか、どうすればジェネリックメッセンジャーを書くことができますか?

どうもありがとう!

4

2 に答える 2

1

あなたはメッセンジャーを持っていますが、メッセージを送信していないようです! 適切な封筒なしで内容物を送信しようとしています。送信したい値を実際のメッセージを表すクラスでラップすると、送信しようとしていたすべての値を含むメッセージのタイプにサブスクライブできます。

public class PlayerSpeedChangedMessage {
    public Guid PlayerId { get; set; }
    public int OldSpeed { get; set; }
    public int NewSpeed { get; set; }
}

public class MyMessageHandler {

    public MyMessageHandler() {
        Messenger<PlayerSpeedChangedMessage>.Subscribe(OnDead);
    }

    HandleSpeedChange(PlayerSpeedChangedMessage message) {
        // Do stuff with the message
    }
}
于 2013-11-08T00:44:16.870 に答える
0

C# 開発者にとって、wiki の Message クラスは少し時代遅れだと思います。C# だけでなく、Unity 自体にもかなり優れたメッセージング システムが既に用意されています (ニーズが複雑すぎない限り)。SendMessageBroadcastMessageを確認してください。

于 2013-11-08T00:28:56.490 に答える