Unity3D監聽事件

用法

編寫一個監聽事件

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

註冊一個監聽事件

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

取消註冊(刪除)一個監聽事件

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

註冊回調返回

  void OnDisable()
    {
        Messenger.AddListener<int>("getScore", GetScore);
    }

    int GetScore()
    {
        return 0;
    }
 void OnDisable()
    {
        Messenger.Broadcast<int>("getScore", OnGetScore);
    }

    void OnGetScore(int i)
    {
        Debug.Log(string.Format("Score is {0}...", i));
    }

廣播一個事件

if (speed != lastSpeed)
    {
        Messenger<float>.Broadcast("speed changed", speed);
    }

代碼

Messenger.cs

using System;
using System.Collections.Generic;
using System.Linq;

public enum MessengerMode {
    DONT_REQUIRE_LISTENER,
    REQUIRE_LISTENER,
}

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

    static public void AddListener(string eventType, Delegate callback) {
        MessengerInternal.OnListenerAdding(eventType, callback);
        eventTable[eventType] = Delegate.Combine(eventTable[eventType], callback);
    }

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

    static public T[] GetInvocationList<T>(string eventType) {
        Delegate d;
        if(eventTable.TryGetValue(eventType, out d)) {
            try {
                return d.GetInvocationList().Cast<T>().ToArray();
            } catch {
                throw MessengerInternal.CreateBroadcastSignatureException(eventType);
            }
        }
        return null;
    }

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

        var 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)) {
            var 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 MessengerInternal.BroadcastException(string.Format("Broadcasting message {0} but no listener found.", eventType));
        }
    }

    static public BroadcastException CreateBroadcastSignatureException(string eventType) {
        return new BroadcastException(string.Format("Broadcasting message {0} but listeners have a different signature than the broadcaster.", eventType));
    }

    public class BroadcastException : Exception {
        public BroadcastException(string msg)
            : base(msg) {
        }
    }

    public class ListenerException : Exception {
        public ListenerException(string msg)
            : base(msg) {
        }
    }
}

// No parameters
static public class Messenger { 
    static public void AddListener(string eventType, Action handler) {
        MessengerInternal.AddListener(eventType, handler);
    }

    static public void AddListener<TReturn>(string eventType, Func<TReturn> handler) {
        MessengerInternal.AddListener(eventType, handler);
    }

    static public void RemoveListener(string eventType, Action handler) {
        MessengerInternal.RemoveListener(eventType, handler);
    }

    static public void RemoveListener<TReturn>(string eventType, Func<TReturn> handler) {
        MessengerInternal.RemoveListener(eventType, handler);
    }

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

    static public void Broadcast<TReturn>(string eventType, Action<TReturn> returnCall) {
        Broadcast(eventType, returnCall, MessengerInternal.DEFAULT_MODE);
    }

    static public void Broadcast(string eventType, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        var invocationList = MessengerInternal.GetInvocationList<Action>(eventType);

        foreach(var callback in invocationList)
            callback.Invoke();
    }

    static public void Broadcast<TReturn>(string eventType, Action<TReturn> returnCall, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        var invocationList = MessengerInternal.GetInvocationList<Func<TReturn>>(eventType);

        foreach(var result in invocationList.Select(del => del.Invoke()).Cast<TReturn>()) {
            returnCall.Invoke(result);
        }
    }
}

// One parameter
static public class Messenger<T> {
    static public void AddListener(string eventType, Action<T> handler) {
        MessengerInternal.AddListener(eventType, handler);
    }

    static public void AddListener<TReturn>(string eventType, Func<T, TReturn> handler) {
        MessengerInternal.AddListener(eventType, handler);
    }

    static public void RemoveListener(string eventType, Action<T> handler) {
        MessengerInternal.RemoveListener(eventType, handler);
    }

    static public void RemoveListener<TReturn>(string eventType, Func<T, TReturn> handler) {
        MessengerInternal.RemoveListener(eventType, handler);
    }

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

    static public void Broadcast<TReturn>(string eventType, T arg1, Action<TReturn> returnCall) {
        Broadcast(eventType, arg1, returnCall, MessengerInternal.DEFAULT_MODE);
    }

    static public void Broadcast(string eventType, T arg1, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        var invocationList = MessengerInternal.GetInvocationList<Action<T>>(eventType);

        foreach(var callback in invocationList)
            callback.Invoke(arg1);
    }

    static public void Broadcast<TReturn>(string eventType, T arg1, Action<TReturn> returnCall, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        var invocationList = MessengerInternal.GetInvocationList<Func<T, TReturn>>(eventType);

        foreach(var result in invocationList.Select(del => del.Invoke(arg1)).Cast<TReturn>()) {
            returnCall.Invoke(result);
        }
    }
}


// Two parameters
static public class Messenger<T, U> { 
    static public void AddListener(string eventType, Action<T, U> handler) {
        MessengerInternal.AddListener(eventType, handler);
    }

    static public void AddListener<TReturn>(string eventType, Func<T, U, TReturn> handler) {
        MessengerInternal.AddListener(eventType, handler);
    }

    static public void RemoveListener(string eventType, Action<T, U> handler) {
        MessengerInternal.RemoveListener(eventType, handler);
    }

    static public void RemoveListener<TReturn>(string eventType, Func<T, U, TReturn> handler) {
        MessengerInternal.RemoveListener(eventType, handler);
    }

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

    static public void Broadcast<TReturn>(string eventType, T arg1, U arg2, Action<TReturn> returnCall) {
        Broadcast(eventType, arg1, arg2, returnCall, MessengerInternal.DEFAULT_MODE);
    }

    static public void Broadcast(string eventType, T arg1, U arg2, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        var invocationList = MessengerInternal.GetInvocationList<Action<T, U>>(eventType);

        foreach(var callback in invocationList)
            callback.Invoke(arg1, arg2);
    }

    static public void Broadcast<TReturn>(string eventType, T arg1, U arg2, Action<TReturn> returnCall, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        var invocationList = MessengerInternal.GetInvocationList<Func<T, U, TReturn>>(eventType);

        foreach(var result in invocationList.Select(del => del.Invoke(arg1, arg2)).Cast<TReturn>()) {
            returnCall.Invoke(result);
        }
    }
}


// Three parameters
static public class Messenger<T, U, V> { 
    static public void AddListener(string eventType, Action<T, U, V> handler) {
        MessengerInternal.AddListener(eventType, handler);
    }

    static public void AddListener<TReturn>(string eventType, Func<T, U, V, TReturn> handler) {
        MessengerInternal.AddListener(eventType, handler);
    }

    static public void RemoveListener(string eventType, Action<T, U, V> handler) {
        MessengerInternal.RemoveListener(eventType, handler);
    }

    static public void RemoveListener<TReturn>(string eventType, Func<T, U, V, TReturn> handler) {
        MessengerInternal.RemoveListener(eventType, handler);
    }

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

    static public void Broadcast<TReturn>(string eventType, T arg1, U arg2, V arg3, Action<TReturn> returnCall) {
        Broadcast(eventType, arg1, arg2, arg3, returnCall, MessengerInternal.DEFAULT_MODE);
    }

    static public void Broadcast(string eventType, T arg1, U arg2, V arg3, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        var invocationList = MessengerInternal.GetInvocationList<Action<T, U, V>>(eventType);

        foreach(var callback in invocationList)
            callback.Invoke(arg1, arg2, arg3);
    }

    static public void Broadcast<TReturn>(string eventType, T arg1, U arg2, V arg3, Action<TReturn> returnCall, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        var invocationList = MessengerInternal.GetInvocationList<Func<T, U, V, TReturn>>(eventType);

        foreach(var result in invocationList.Select(del => del.Invoke(arg1, arg2, arg3)).Cast<TReturn>()) {
            returnCall.Invoke(result);
        }
    }
}

MessengerUnitTest.cs

using System;

class MessengerUnitTest {

    private readonly string eventType1 = "__testEvent1";
    private readonly string eventType2 = "__testEvent2";

    bool wasCalled = false;

    public void RunTest() {
        RunAddTests();
        RunBroadcastTests();
        RunRemoveTests();
        Console.Out.WriteLine("All Messenger tests passed.");
    }


    private void RunAddTests() {
        Messenger.AddListener(eventType1, TestCallback);

        try {
            // This should fail because we're adding a new event listener for same event type but a different delegate signature
            Messenger<float>.AddListener(eventType1, TestCallbackFloat);
            throw new Exception("Unit test failure - expected a ListenerException");
        } catch (MessengerInternal.ListenerException e) {
            // All good
        }

        Messenger<float>.AddListener(eventType2, TestCallbackFloat);
    }


    private void RunBroadcastTests() {
        wasCalled = false;
        Messenger.Broadcast(eventType1);
        if (!wasCalled) { throw new Exception("Unit test failure - event handler appears to have not been called."); }
        wasCalled = false;
        Messenger<float>.Broadcast(eventType2, 1.0f);
        if (!wasCalled) { throw new Exception("Unit test failure - event handler appears to have not been called."); }

        // No listener should exist for this event, but we don't require a listener so it should pass
        Messenger<float>.Broadcast(eventType2 + "_", 1.0f, MessengerMode.DONT_REQUIRE_LISTENER);

        try {
            // Broadcasting for an event there exists listeners for, but using wrong signature
            Messenger<float>.Broadcast(eventType1, 1.0f, MessengerMode.DONT_REQUIRE_LISTENER);
            throw new Exception("Unit test failure - expected a BroadcastException");
        }
        catch (MessengerInternal.BroadcastException e) {
            // All good
        }

        try {
            // Same thing, but now we (implicitly) require at least one listener
            Messenger<float>.Broadcast(eventType2 + "_", 1.0f);
            throw new Exception("Unit test failure - expected a BroadcastException");
        } catch (MessengerInternal.BroadcastException e) {
            // All good
        }

        try {
            // Wrong generic type for this broadcast, and we implicitly require a listener
            Messenger<double>.Broadcast(eventType2, 1.0);
            throw new Exception("Unit test failure - expected a BroadcastException");
        } catch (MessengerInternal.BroadcastException e) {
            // All good
        }

    }


    private void RunRemoveTests() {

        try {
            // Removal with wrong signature should fail
            Messenger<float>.RemoveListener(eventType1, TestCallbackFloat);
            throw new Exception("Unit test failure - expected a ListenerException");
        }
        catch (MessengerInternal.ListenerException e) {
            // All good
        }

        Messenger.RemoveListener(eventType1, TestCallback);

        try {
            // Repeated removal should fail
            Messenger.RemoveListener(eventType1, TestCallback);
            throw new Exception("Unit test failure - expected a ListenerException");
        }
        catch (MessengerInternal.ListenerException e) {
            // All good
        }



        Messenger<float>.RemoveListener(eventType2, TestCallbackFloat);

        try {
            // Repeated removal should fail
            Messenger<float>.RemoveListener(eventType2, TestCallbackFloat);
            throw new Exception("Unit test failure - expected a ListenerException");
        }
        catch (MessengerInternal.ListenerException e) {
            // All good
        }
    }


    void TestCallback() {
        wasCalled = true;
        Console.Out.WriteLine("TestCallback() was called.");
    }

    void TestCallbackFloat(float f) {
        wasCalled = true;
        Console.Out.WriteLine("TestCallbackFloat(float) was called.");

        if (f != 1.0f) {
            throw new Exception("Unit test failure - wrong value on float argument");
        }
    }



}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章