Skip to content

Instantly share code, notes, and snippets.

@mikecann
Last active August 29, 2015 14:00
Show Gist options
  • Save mikecann/11337311 to your computer and use it in GitHub Desktop.
Save mikecann/11337311 to your computer and use it in GitHub Desktop.
Typesafe Automatic Listener Removal in Strange IoC
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));
}
}
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);
}
}
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);
}
}
}
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