-
Notifications
You must be signed in to change notification settings - Fork 0
/
SignalBase.cs
197 lines (167 loc) · 3.85 KB
/
SignalBase.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
using System;
using System.Collections.Generic;
namespace Atlas.Signals;
public abstract class SignalBase : ISignalBase, IDisposable
{
private readonly List<SlotBase> slots = new();
private readonly Stack<SlotBase> slotsPooled = new();
private readonly Stack<SlotBase> slotsRemoved = new();
public int Dispatching { get; private set; } = 0;
public bool IsDisposed { get; private set; } = false;
public void Dispose()
{
if(IsDisposed)
return;
IsDisposed = true;
slotsPooled.Clear();
RemoveAll();
}
public bool IsDispatching => Dispatching > 0;
public IReadOnlyList<ISlotBase> Slots => new List<SlotBase>(slots);
private void DisposeSlot(SlotBase slot)
{
slot.Signal = null;
slot.Dispose();
if(!IsDisposed)
slotsPooled.Push(slot);
}
public ISlotBase Get(Delegate listener)
{
if(listener == null)
return null;
foreach(var slot in slots)
{
if(slot.Listener == listener)
return slot;
}
return null;
}
public ISlotBase Get(int index)
{
if(index < 0)
return null;
if(index > slots.Count - 1)
return null;
return slots[index];
}
public int GetIndex(Delegate listener)
{
if(listener == null)
return -1;
for(int index = slots.Count - 1; index > -1; --index)
{
if(slots[index].Listener == listener)
return index;
}
return -1;
}
public ISlotBase Add(Delegate listener) => Add(listener, 0);
public ISlotBase Add(Delegate listener, int priority)
{
if(listener == null)
return null;
var slot = (SlotBase)Get(listener);
if(slot == null)
{
if(slotsPooled.Count > 0)
slot = slotsPooled.Pop();
else
slot = CreateSlot();
}
slot.Signal = this;
slot.Listener = listener;
slot.Priority = priority;
slot.IsRemoved = false;
Prioritize(slot);
IsDisposed = false;
return slot;
}
protected abstract SlotBase CreateSlot();
internal void Prioritize(SlotBase slot)
{
slots.Remove(slot);
for(var index = slots.Count; index > 0; --index)
{
if(slots[index - 1].Priority <= slot.Priority)
{
slots.Insert(index, slot);
return;
}
}
slots.Insert(0, slot);
}
public bool Remove(Delegate listener)
{
if(listener != null)
{
for(int index = slots.Count - 1; index > -1; --index)
{
if(slots[index].Listener == listener)
{
return Remove(index);
}
}
}
return false;
}
public bool Remove(int index)
{
if(index < 0)
return false;
if(index >= slots.Count)
return false;
var slot = slots[index];
slot.IsRemoved = true;
slots.RemoveAt(index);
if(Dispatching > 0)
{
slotsRemoved.Push(slot);
}
else
{
DisposeSlot(slot);
}
return true;
}
public bool RemoveAll()
{
if(slots.Count <= 0)
return false;
while(slots.Count > 0)
Remove(slots.Count - 1);
return true;
}
protected bool Dispatch<TSlot>(Action<TSlot> dispatcher)
where TSlot : SlotBase
{
if(slots.Count <= 0)
return false;
++Dispatching;
foreach(TSlot slot in Slots)
{
if(slot.IsRemoved)
continue;
dispatcher.Invoke(slot);
}
if(--Dispatching == 0)
{
while(slotsRemoved.Count > 0)
DisposeSlot(slotsRemoved.Pop());
}
return true;
}
}
public abstract class SignalBase<TSlot, TISlot, TDelegate> : SignalBase, ISignalBase<TISlot, TDelegate>
where TSlot : SlotBase, TISlot, new()
where TISlot : ISlotBase
where TDelegate : Delegate
{
public TISlot Add(TDelegate listener) => (TISlot)base.Add(listener, 0);
public TISlot Add(TDelegate listener, int priority) => (TISlot)base.Add(listener, priority);
public TISlot Get(TDelegate listener) => (TISlot)base.Get(listener);
public int GetIndex(TDelegate listener) => base.GetIndex(listener);
public bool Remove(TDelegate listener) => base.Remove(listener);
public new TISlot Get(int index) => (TISlot)base.Get(index);
protected override SlotBase CreateSlot() => new TSlot();
protected bool Dispatch(Action<TSlot> dispatcher) => Dispatch<TSlot>(dispatcher);
}