Last active
August 29, 2015 14:00
-
-
Save mikecann/11337311 to your computer and use it in GitHub Desktop.
Typesafe Automatic Listener Removal in Strange IoC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BaseMediator<TView> : Mediator | |
{ | |
[Inject] | |
public TView View { get; set; } | |
protected List<SignalBinding> signalBindings = new List<SignalBinding>(); | |
override public void OnRegister() | |
{ | |
} | |
override public void OnRemove() | |
{ | |
signalBindings.ForEach(r => r.removeCallback()); | |
} | |
protected void RegisterListener(Signal signal, Action callback) | |
{ | |
signalBindings.Add(signal.AddListener(callback)); | |
} | |
protected void RegisterListener<T>(Signal<T> signal, Action<T> callback) | |
{ | |
signalBindings.Add(signal.AddListener(callback)); | |
} | |
protected void RegisterListener<T,U>(Signal<T,U> signal, Action<T,U> callback) | |
{ | |
signalBindings.Add(signal.AddListener(callback)); | |
} | |
protected void RegisterListener<T, U, V>(Signal<T, U, V> signal, Action<T, U, V> callback) | |
{ | |
signalBindings.Add(signal.AddListener(callback)); | |
} | |
protected void RegisterListener<T, U, V, W>(Signal<T, U, V, W> signal, Action<T, U, V, W> callback) | |
{ | |
signalBindings.Add(signal.AddListener(callback)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MockView : View | |
{ | |
public Signal noParmSignal = new Signal(); | |
public Signal<int> oneParamSignal = new Signal<int>(); | |
public Signal<int, string> twoParamSignal = new Signal<int, string>(); | |
public Signal<int, string, bool> threeParamSignal = new Signal<int, string, bool>(); | |
public Signal<int, string, bool, float> fourParamSignal = new Signal<int, string, bool, float>(); | |
} | |
public class MockMediator : BaseMediator<MockView> | |
{ | |
public bool noParamCalled = false; | |
public bool oneParamCalled = false; | |
public int oneParamValue = -1; | |
public bool twoParamCalled = false; | |
public int twoParamValueA = -1; | |
public string twoParamValueB = null; | |
public bool threeParamCalled = false; | |
public int threeParamValueA = -1; | |
public string threeParamValueB = null; | |
public bool threeParamValueC = false; | |
public bool fourParamCalled = false; | |
public int fourParamValueA = -1; | |
public string fourParamValueB = null; | |
public bool fourParamValueC = false; | |
public float fourParamValueD = -1.1f; | |
public override void OnRegister() | |
{ | |
RegisterListener(View.noParmSignal, () => noParamCalled=true); | |
RegisterListener(View.oneParamSignal, a => | |
{ | |
oneParamCalled = true; | |
oneParamValue = a; | |
}); | |
RegisterListener(View.twoParamSignal, (a, b) => | |
{ | |
twoParamCalled = true; | |
twoParamValueA = a; | |
twoParamValueB = b; | |
}); | |
RegisterListener(View.threeParamSignal, (a, b, c) => | |
{ | |
threeParamCalled = true; | |
threeParamValueA = a; | |
threeParamValueB = b; | |
threeParamValueC = c; | |
}); | |
RegisterListener(View.fourParamSignal, (a, b, c, d) => | |
{ | |
fourParamCalled = true; | |
fourParamValueA = a; | |
fourParamValueB = b; | |
fourParamValueC = c; | |
fourParamValueD = d; | |
}); | |
} | |
} | |
public class BaseMediatorTests : BaseUnitTest | |
{ | |
private InjectionBinder injector; | |
private MockMediator mediator; | |
private MockView view; | |
[SetUp] | |
public void Init() | |
{ | |
view = new MockView(); | |
injector = new InjectionBinder(); | |
injector.Bind<GameObject>().ToName(ContextKeys.CONTEXT_VIEW).ToValue(new GameObject()); | |
injector.Bind<MockView>().ToValue(view); | |
injector.Bind<MockMediator>().ToSingleton(); | |
mediator = injector.GetInstance<MockMediator>(); | |
mediator.OnRegister(); | |
} | |
[Test] | |
public void NothingCalled() | |
{ | |
Assert.AreEqual(false, mediator.noParamCalled); | |
Assert.AreEqual(false, mediator.oneParamCalled); | |
Assert.AreEqual(false, mediator.twoParamCalled); | |
Assert.AreEqual(false, mediator.threeParamCalled); | |
Assert.AreEqual(false, mediator.fourParamCalled); | |
} | |
[Test] | |
public void CallbacksGetCalled() | |
{ | |
view.noParmSignal.Dispatch(); | |
Assert.AreEqual(true, mediator.noParamCalled); | |
view.oneParamSignal.Dispatch(12345); | |
Assert.AreEqual(true, mediator.oneParamCalled); | |
Assert.AreEqual(12345, mediator.oneParamValue); | |
view.twoParamSignal.Dispatch(12345,"hello"); | |
Assert.AreEqual(true, mediator.twoParamCalled); | |
Assert.AreEqual(12345, mediator.twoParamValueA); | |
Assert.AreEqual("hello", mediator.twoParamValueB); | |
view.threeParamSignal.Dispatch(12345, "hello", true); | |
Assert.AreEqual(true, mediator.threeParamCalled); | |
Assert.AreEqual(12345, mediator.threeParamValueA); | |
Assert.AreEqual("hello", mediator.threeParamValueB); | |
Assert.AreEqual(true, mediator.threeParamValueC); | |
view.fourParamSignal.Dispatch(12345, "hello", true, 12.345f); | |
Assert.AreEqual(true, mediator.fourParamCalled); | |
Assert.AreEqual(12345, mediator.fourParamValueA); | |
Assert.AreEqual("hello", mediator.fourParamValueB); | |
Assert.AreEqual(true, mediator.fourParamValueC); | |
Assert.AreEqual(12.345f, mediator.fourParamValueD); | |
} | |
[Test] | |
public void CallbacksDoNotGetCalledAfterRemoval() | |
{ | |
mediator.OnRemove(); | |
view.noParmSignal.Dispatch(); | |
Assert.AreEqual(false, mediator.noParamCalled); | |
view.oneParamSignal.Dispatch(12345); | |
Assert.AreEqual(false, mediator.oneParamCalled); | |
view.twoParamSignal.Dispatch(12345, "hello"); | |
Assert.AreEqual(false, mediator.twoParamCalled); | |
view.threeParamSignal.Dispatch(12345, "hello", true); | |
Assert.AreEqual(false, mediator.threeParamCalled); | |
view.fourParamSignal.Dispatch(12345, "hello", true, 12.345f); | |
Assert.AreEqual(false, mediator.fourParamCalled); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace strange.extensions.signal.impl | |
{ | |
public class SignalBinding | |
{ | |
public Action removeCallback; | |
} | |
/// Base concrete form for a Signal with no parameters | |
public class Signal : BaseSignal | |
{ | |
public event Action Listener = delegate { }; | |
public event Action OnceListener = delegate { }; | |
public SignalBinding AddListener(Action callback) | |
{ | |
Listener += callback; | |
return new SignalBinding() | |
{ | |
removeCallback = () => | |
{ | |
RemoveListener(callback); | |
} | |
}; | |
} | |
public Action AddOnce(Action callback) { OnceListener += callback; ; return callback; } | |
public void RemoveListener(Action callback) { Listener -= callback; } | |
public override List<Type> GetTypes() | |
{ | |
return new List<Type>(); | |
} | |
public void Dispatch() | |
{ | |
Listener(); | |
OnceListener(); | |
OnceListener = delegate { }; | |
base.Dispatch(null); | |
} | |
} | |
/// Base concrete form for a Signal with one parameter | |
public class Signal<T> : BaseSignal | |
{ | |
public event Action<T> Listener = delegate { }; | |
public event Action<T> OnceListener = delegate { }; | |
public SignalBinding AddListener(Action<T> callback) | |
{ | |
Listener += callback; | |
return new SignalBinding() | |
{ | |
removeCallback = () => | |
{ | |
RemoveListener(callback); | |
} | |
}; | |
} | |
public Action<T> AddOnce(Action<T> callback) { OnceListener += callback; return callback; } | |
public void RemoveListener(Action<T> callback) { Listener -= callback; } | |
public override List<Type> GetTypes() | |
{ | |
List<Type> retv = new List<Type>(); | |
retv.Add(typeof(T)); | |
return retv; | |
} | |
public void Dispatch(T type1) | |
{ | |
Listener(type1); | |
OnceListener(type1); | |
OnceListener = delegate { }; | |
object[] outv = { type1 }; | |
base.Dispatch(outv); | |
} | |
} | |
/// Base concrete form for a Signal with two parameters | |
public class Signal<T, U> : BaseSignal | |
{ | |
public event Action<T, U> Listener = delegate { }; | |
public event Action<T, U> OnceListener = delegate { }; | |
public SignalBinding AddListener(Action<T, U> callback) | |
{ | |
Listener += callback; | |
return new SignalBinding() | |
{ | |
removeCallback = () => | |
{ | |
RemoveListener(callback); | |
} | |
}; | |
} | |
public void AddOnce(Action<T, U> callback) { OnceListener += callback; } | |
public void RemoveListener(Action<T, U> callback) { Listener -= callback; } | |
public override List<Type> GetTypes() | |
{ | |
List<Type> retv = new List<Type>(); | |
retv.Add(typeof(T)); | |
retv.Add(typeof(U)); | |
return retv; | |
} | |
public void Dispatch(T type1, U type2) | |
{ | |
Listener(type1, type2); | |
OnceListener(type1, type2); | |
OnceListener = delegate { }; | |
object[] outv = { type1, type2 }; | |
base.Dispatch(outv); | |
} | |
} | |
/// Base concrete form for a Signal with three parameters | |
public class Signal<T, U, V> : BaseSignal | |
{ | |
public event Action<T, U, V> Listener = delegate { }; | |
public event Action<T, U, V> OnceListener = delegate { }; | |
public SignalBinding AddListener(Action<T, U, V> callback) | |
{ | |
Listener += callback; | |
return new SignalBinding() | |
{ | |
removeCallback = () => | |
{ | |
RemoveListener(callback); | |
} | |
}; | |
} | |
public void AddOnce(Action<T, U, V> callback) { OnceListener += callback; } | |
public void RemoveListener(Action<T, U, V> callback) { Listener -= callback; } | |
public override List<Type> GetTypes() | |
{ | |
List<Type> retv = new List<Type>(); | |
retv.Add(typeof(T)); | |
retv.Add(typeof(U)); | |
retv.Add(typeof(V)); | |
return retv; | |
} | |
public void Dispatch(T type1, U type2, V type3) | |
{ | |
Listener(type1, type2, type3); | |
OnceListener(type1, type2, type3); | |
OnceListener = delegate { }; | |
object[] outv = { type1, type2, type3 }; | |
base.Dispatch(outv); | |
} | |
} | |
/// Base concrete form for a Signal with four parameters | |
public class Signal<T, U, V, W> : BaseSignal | |
{ | |
public event Action<T, U, V, W> Listener = delegate { }; | |
public event Action<T, U, V, W> OnceListener = delegate { }; | |
public SignalBinding AddListener(Action<T, U, V, W> callback) | |
{ | |
Listener += callback; | |
return new SignalBinding() | |
{ | |
removeCallback = () => | |
{ | |
RemoveListener(callback); | |
} | |
}; | |
} | |
public void AddOnce(Action<T, U, V, W> callback) { OnceListener += callback; } | |
public void RemoveListener(Action<T, U, V, W> callback) { Listener -= callback; } | |
public override List<Type> GetTypes() | |
{ | |
List<Type> retv = new List<Type>(); | |
retv.Add(typeof(T)); | |
retv.Add(typeof(U)); | |
retv.Add(typeof(V)); | |
retv.Add(typeof(W)); | |
return retv; | |
} | |
public void Dispatch(T type1, U type2, V type3, W type4) | |
{ | |
Listener(type1, type2, type3, type4); | |
OnceListener(type1, type2, type3, type4); | |
OnceListener = delegate { }; | |
object[] outv = { type1, type2, type3, type4 }; | |
base.Dispatch(outv); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class MyMediator : BaseMediator<MyView> | |
{ | |
public override void OnRegister() | |
{ | |
RegisterListener(View.MySignal1, () => Debug.Log("Hello World")); | |
RegisterListener(View.MySignal2, OnViewSignal2); | |
} | |
private void OnViewSignal2(string a) | |
{ | |
Debug.Log("Hello "+a) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment