@@ -25,23 +25,32 @@ public interface IMessageBrokerService
2525 /// Publish a message in the message broker.
2626 /// If there is no object subscribing the message type, nothing will happen
2727 /// </summary>
28+ /// <remarks>
29+ /// Use <see cref="PublishSafe{T}(T)"/> it there are a chain subscriptions during publishing
30+ /// </remarks>
2831 void Publish < T > ( T message ) where T : IMessage ;
29-
32+
33+ /// <summary>
34+ /// Publish a message in the message broker.
35+ /// If there is no object subscribing the message type, nothing will happen
36+ /// </summary>
37+ /// <remarks>
38+ /// This method can be slow and allocated extra memory if there are a lot of subscribers to the <typeparamref name="T"/>.
39+ /// Use <see cref="Publish{T}(T)"/> instead for faster iteration speed IF and ONLY IF there aren't chain subscriptions during publishing
40+ /// </remarks>
41+ void PublishSafe < T > ( T message ) where T : IMessage ;
42+
3043 /// <summary>
3144 /// Subscribes to the message type.
3245 /// Will invoke the <paramref name="action"/> every time the message of the subscribed type is published.
3346 /// </summary>
3447 void Subscribe < T > ( Action < T > action ) where T : IMessage ;
35-
36- /// <summary>
37- /// Unsubscribe the <paramref name="action"/> from the message broker.
38- /// </summary>
39- void Unsubscribe < T > ( Action < T > action ) where T : IMessage ;
40-
48+
4149 /// <summary>
42- /// Unsubscribe all actions from the message broker from of the given message type.
50+ /// Unsubscribe the action of <typeparamref name="T"/> from the <paramref name="subscriber"/> in the message broker.
51+ /// If <paramref name="subscriber"/> is null then will unsubscribe from ALL subscribers currently subscribed to <typeparamref name="T"/>
4352 /// </summary>
44- void Unsubscribe < T > ( ) where T : IMessage ;
53+ void Unsubscribe < T > ( object subscriber = null ) where T : IMessage ;
4554
4655 /// <summary>
4756 /// Unsubscribe from all messages.
@@ -53,7 +62,9 @@ public interface IMessageBrokerService
5362 /// <inheritdoc />
5463 public class MessageBrokerService : IMessageBrokerService
5564 {
56- private readonly IDictionary < Type , IDictionary < object , IList > > _subscriptions = new Dictionary < Type , IDictionary < object , IList > > ( ) ;
65+ private readonly IDictionary < Type , IDictionary < object , Delegate > > _subscriptions = new Dictionary < Type , IDictionary < object , Delegate > > ( ) ;
66+
67+ private ( bool , IMessage ) _isPublishing ;
5768
5869 /// <inheritdoc />
5970 public void Publish < T > ( T message ) where T : IMessage
@@ -63,18 +74,35 @@ public void Publish<T>(T message) where T : IMessage
6374 return ;
6475 }
6576
66- var subscriptionCopy = new IList [ subscriptionObjects . Count ] ;
67-
77+ _isPublishing = ( true , message ) ;
78+
79+ foreach ( var subscription in subscriptionObjects )
80+ {
81+ var action = ( Action < T > ) subscription . Value ;
82+
83+ action ( message ) ;
84+ }
85+
86+ _isPublishing = ( false , message ) ;
87+ }
88+
89+ /// <inheritdoc />
90+ public void PublishSafe < T > ( T message ) where T : IMessage
91+ {
92+ if ( ! _subscriptions . TryGetValue ( typeof ( T ) , out var subscriptionObjects ) )
93+ {
94+ return ;
95+ }
96+
97+ var subscriptionCopy = new Delegate [ subscriptionObjects . Count ] ;
98+
6899 subscriptionObjects . Values . CopyTo ( subscriptionCopy , 0 ) ;
69100
70101 for ( var i = 0 ; i < subscriptionCopy . Length ; i ++ )
71102 {
72- var actions = ( List < Action < T > > ) subscriptionCopy [ i ] ;
103+ var action = ( Action < T > ) subscriptionCopy [ i ] ;
73104
74- for ( var index = 0 ; index < actions . Count ; index ++ )
75- {
76- actions [ index ] ( message ) ;
77- }
105+ action ( message ) ;
78106 }
79107 }
80108
@@ -88,58 +116,51 @@ public void Subscribe<T>(Action<T> action) where T : IMessage
88116 {
89117 throw new ArgumentException ( "Subscribe static functions to a message is not supported!" ) ;
90118 }
91-
92- if ( ! _subscriptions . TryGetValue ( type , out var subscriptionObjects ) )
119+ if ( _isPublishing . Item1 )
93120 {
94- subscriptionObjects = new Dictionary < object , IList > ( ) ;
95- _subscriptions . Add ( type , subscriptionObjects ) ;
121+ throw new InvalidOperationException ( $ "Cannot subscribe to { type . Name } message while publishing " +
122+ $ " { _isPublishing . Item2 . GetType ( ) . Name } message. Use { nameof ( PublishSafe ) } instead!" ) ;
96123 }
97124
98- if ( ! subscriptionObjects . TryGetValue ( subscriber , out IList actions ) )
125+ if ( ! _subscriptions . TryGetValue ( type , out var subscriptionObjects ) )
99126 {
100- actions = new List < Action < T > > ( ) ;
101- subscriptionObjects . Add ( subscriber , actions ) ;
127+ subscriptionObjects = new Dictionary < object , Delegate > ( ) ;
128+ _subscriptions . Add ( type , subscriptionObjects ) ;
102129 }
103130
104- actions . Add ( action ) ;
131+ subscriptionObjects [ subscriber ] = action ;
105132 }
106133
107134 /// <inheritdoc />
108- public void Unsubscribe < T > ( Action < T > action ) where T : IMessage
135+ public void Unsubscribe < T > ( object subscriber = null ) where T : IMessage
109136 {
110137 var type = typeof ( T ) ;
111- var subscriber = action . Target ;
112138
113139 if ( subscriber == null )
114140 {
115- throw new ArgumentException ( "Subscribe static functions to a message is not supported!" ) ;
116- }
141+ _subscriptions . Remove ( type ) ;
117142
118- if ( ! _subscriptions . TryGetValue ( type , out var subscriptionObjects ) ||
119- ! subscriptionObjects . TryGetValue ( subscriber , out var actions ) )
120- {
121143 return ;
122144 }
123145
124- actions . Remove ( action ) ;
125-
126- if ( actions . Count == 0 )
146+ if ( _isPublishing . Item1 )
127147 {
128- subscriptionObjects . Remove ( subscriber ) ;
148+ throw new InvalidOperationException ( $ "Cannot unsubscribe to { type . Name } message while publishing " +
149+ $ "{ _isPublishing . Item2 . GetType ( ) . Name } message. Use { nameof ( PublishSafe ) } instead!") ;
129150 }
151+ if ( ! _subscriptions . TryGetValue ( type , out var subscriptionObjects ) )
152+ {
153+ return ;
154+ }
155+
156+ subscriptionObjects . Remove ( subscriber ) ;
130157
131158 if ( subscriptionObjects . Count == 0 )
132159 {
133160 _subscriptions . Remove ( type ) ;
134161 }
135162 }
136163
137- /// <inheritdoc />
138- public void Unsubscribe < T > ( ) where T : IMessage
139- {
140- _subscriptions . Remove ( typeof ( T ) ) ;
141- }
142-
143164 /// <inheritdoc />
144165 public void UnsubscribeAll ( object subscriber = null )
145166 {
@@ -149,12 +170,14 @@ public void UnsubscribeAll(object subscriber = null)
149170 return ;
150171 }
151172
173+ if ( _isPublishing . Item1 )
174+ {
175+ throw new InvalidOperationException ( $ "Cannot unsubscribe from { subscriber } message while publishing " +
176+ $ "{ _isPublishing . Item2 . GetType ( ) . Name } message. Use { nameof ( PublishSafe ) } instead!") ;
177+ }
152178 foreach ( var subscriptionObjects in _subscriptions . Values )
153179 {
154- if ( subscriptionObjects . ContainsKey ( subscriber ) )
155- {
156- subscriptionObjects . Remove ( subscriber ) ;
157- }
180+ subscriptionObjects . Remove ( subscriber ) ;
158181 }
159182 }
160183 }
0 commit comments