3

我不确定以前是否有人问过这个问题,但我真的不知道如何寻找它,因为我不确定这件事/我正在尝试做的事情到底是什么......

我有一个基于委托的消息通用系统,我在 Unity3D 中使用 - 取自此处

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

现在,我有通用的信使类,有一个、两个、三个甚至没有参数——所以用户可以选择一个合适的事件处理程序来订阅一个事件。

这是不带通用参数的版本:

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

这是需要一个 arg 的版本,(我只是复制粘贴并添加一个 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);
            }
        }
    }
}

正如您可能已经猜到的那样,那个需要两个 args 的,我只是再次复制粘贴,然后添加另一种泛型类型,例如<T, U>等。

这是我要消除的部分-但我不知道如何消除。更准确地说,我正在寻找的是:只有一Messenger堂课,但我能够做到:

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

你明白了……

我该怎么做,如何编写通用信使,当我想添加另一个通用 arg 时,我不必复制粘贴并编写一个完整的其他版本,只是因为我需要一个额外的 arg?

非常感谢!

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 回答