Skip to content

Commit 271af69

Browse files
committed
Interface compatibility for signals
Normally you can not declare concrete interfaces and then subscribe to their interface types and you realice you are coupling your concrete signal types to the receivers for example. Lets say I have a player and i want to save the game when i finish a level Ok easy I create " signalLevelCompleted" and then I subscribe it to my "SaveGameSystem" then I also want to save when i reach a checkpoint Again i create "SignalCheckpointReached" and then I subscribe it to my "SaveGameSystem" And then you realize you are coupling the types "signalLevelCompleted" and "SignalCheckpointReached" to your "SaveGameSystem" ohhh its not too good... but lets give the power of the interfaces to the signals So i have the SignalCheckPoint reached and SignalLevelCompleted Both implements ISignalGameSaver My GameSaverSystem just Subscribes to ISignalSaveGame for saving the game So when i shot any of that signals i automatically save the game and if for example i create another signal for Achievements "SignalAchievementCompleted" I just have to implement the interface to it so it saves the game automatically It offers a lot of modularity and abstractions to signals Since you are firing a concrete signal with the action you are doing And giving them functionality trough Interface implementations
1 parent d4a43c2 commit 271af69

File tree

2 files changed

+57
-0
lines changed

2 files changed

+57
-0
lines changed

UnityProject/Assets/Plugins/Zenject/OptionalExtras/Signals/Internal/Binders/SignalExtensions.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ public static DeclareSignalIdRequireHandlerAsyncTickPriorityCopyBinder DeclareSi
3030
return container.DeclareSignal(typeof(TSignal));
3131
}
3232

33+
//If you declare a concrete signal type with their interfaces you can do abstract Firing to get their non concrete subscriptions
34+
public static DeclareSignalIdRequireHandlerAsyncTickPriorityCopyBinder DeclareSignalWithInterfaces<TSignal>(this DiContainer container)
35+
{
36+
Type type = typeof(TSignal);
37+
38+
var declaration = container.DeclareSignal(type);
39+
40+
//Automatic interface declaration
41+
Type[] interfaces = type.GetInterfaces();
42+
int numOfInterfaces = interfaces.Length;
43+
for (int i = 0; i < numOfInterfaces; i++)
44+
{
45+
container.DeclareSignal(interfaces[i]);
46+
}
47+
48+
return declaration;
49+
}
50+
3351
public static BindSignalIdToBinder<TSignal> BindSignal<TSignal>(this DiContainer container)
3452
{
3553
var signalBindInfo = new SignalBindingBindInfo(typeof(TSignal));

UnityProject/Assets/Plugins/Zenject/OptionalExtras/Signals/Main/SignalBus.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,45 @@ public int NumSubscribers
4949
get { return _subscriptionMap.Count; }
5050
}
5151

52+
53+
//AbstractFire Works like a normal Fire but it fires the interface types of the signal too
54+
public void AbstractFire<TSignal>() where TSignal : new() => AbstractFire(new TSignal());
55+
public void AbstractFire<TSignal>(TSignal signal) => AbstractFireId(null, signal);
56+
public void AbstractFireId<TSignal>(object identifier, TSignal signal)
57+
{
58+
// Do this before creating the signal so that it throws if the signal was not declared
59+
Type tt = typeof(TSignal);
60+
var declaration = GetDeclaration(tt, identifier, true);
61+
declaration.Fire(signal);
62+
63+
//Everything is fired like a normal signal and then this method Fires the signal with the interface types
64+
//Its async because its faster and doesn't blocks the main thread when you fire signals
65+
//Tested with a loop of 1 million iteration and this is the faster way of getting the interfaces fast
66+
FireSignalGetDeclarationForInterfacesAsync(identifier, signal, tt);
67+
}
68+
69+
//Fire and forget methof for the task
70+
public async void FireSignalGetDeclarationForInterfacesAsync<TSignal>(object identifier, TSignal signal, Type type)
71+
{
72+
await Task.Run(() => FireSignalGetDeclarationForInterfacesTask(identifier, signal, type));
73+
}
74+
public async Task FireSignalGetDeclarationForInterfacesTask<TSignal>(object identifier, TSignal signal, Type type)
75+
{
76+
//The asynchronous iteration for reflection
77+
Type[] interfaces = type.GetInterfaces();
78+
int numOfInterfaces = interfaces.Length;
79+
for (int i = 0; i < numOfInterfaces; i++)
80+
{
81+
//To make this work you should also declare the signal's interfaces, but they are automatically declared
82+
//if you do "DeclareSignalWithInterfaces<TSignal>()" in the container
83+
//Go to SignalExtensions.cs for more info
84+
var declaration = GetDeclaration(interfaces[i], identifier, true);
85+
declaration.Fire(signal);
86+
}
87+
}
88+
89+
90+
5291
public void LateDispose()
5392
{
5493
if (_settings.RequireStrictUnsubscribe)

0 commit comments

Comments
 (0)