@@ -11,29 +11,42 @@ public sealed class EventInvoker(BotContext context) : IDisposable
1111{
1212 private const string Tag = nameof ( EventInvoker ) ;
1313
14- private readonly ConcurrentDictionary < Type , Delegate > _syncHandlers = new ( ) ;
15- private readonly ConcurrentDictionary < Type , Delegate > _asyncHandlers = new ( ) ;
14+ private static readonly ConcurrentDictionary < Delegate , Delegate > SyncHandlerCache = new ( ) ;
15+ private static readonly ConcurrentDictionary < Delegate , Delegate > AsyncHandlerCache = new ( ) ;
16+
17+ private readonly ConcurrentDictionary < Type , Delegate ? > _syncHandlers = new ( ) ;
18+ private readonly ConcurrentDictionary < Type , Delegate ? > _asyncHandlers = new ( ) ;
1619
1720 public delegate void LagrangeEventHandler < in TEvent > ( BotContext ctx , TEvent e ) where TEvent : EventBase ;
1821 public delegate Task LagrangeAsyncEventHandler < in TEvent > ( BotContext ctx , TEvent e ) where TEvent : EventBase ;
1922
2023 public void RegisterEvent < TEvent > ( LagrangeEventHandler < TEvent > handler ) where TEvent : EventBase
2124 {
22- Debug . Assert ( ! handler . Method . IsStatic ) ;
25+ Debug . Assert ( handler . Method . IsStatic is false ) ;
2326
24- AddHandler ( _syncHandlers , typeof ( TEvent ) , BuildSyncDelegate ( handler ) ) ;
27+ var wrappedHandler = BuildSyncDelegate ( handler ) ;
28+ AddHandler ( _syncHandlers , typeof ( TEvent ) , wrappedHandler ) ;
2529 }
2630
2731 public void RegisterEvent < TEvent > ( LagrangeAsyncEventHandler < TEvent > handler ) where TEvent : EventBase
2832 {
29- Debug . Assert ( ! handler . Method . IsStatic ) ;
33+ Debug . Assert ( handler . Method . IsStatic is false ) ;
3034
31- AddHandler ( _asyncHandlers , typeof ( TEvent ) , BuildAsyncDelegate ( handler ) ) ;
35+ var wrappedHandler = BuildAsyncDelegate ( handler ) ;
36+ AddHandler ( _asyncHandlers , typeof ( TEvent ) , wrappedHandler ) ;
3237 }
3338
34- public void UnregisterEvent < TEvent > ( LagrangeEventHandler < TEvent > handler ) where TEvent : EventBase => RemoveHandler ( _syncHandlers , typeof ( TEvent ) , handler ) ;
39+ public void UnregisterEvent < TEvent > ( LagrangeEventHandler < TEvent > handler ) where TEvent : EventBase
40+ {
41+ var wrappedHandler = BuildSyncDelegate ( handler ) ;
42+ RemoveHandler ( _syncHandlers , typeof ( TEvent ) , wrappedHandler ) ;
43+ }
3544
36- public void UnregisterEvent < TEvent > ( LagrangeAsyncEventHandler < TEvent > handler ) where TEvent : EventBase => RemoveHandler ( _asyncHandlers , typeof ( TEvent ) , handler ) ;
45+ public void UnregisterEvent < TEvent > ( LagrangeAsyncEventHandler < TEvent > handler ) where TEvent : EventBase
46+ {
47+ var wrappedHandler = BuildAsyncDelegate ( handler ) ;
48+ RemoveHandler ( _asyncHandlers , typeof ( TEvent ) , wrappedHandler ) ;
49+ }
3750
3851 public void UnregisterEvent < TEvent > ( ) where TEvent : EventBase
3952 {
@@ -47,12 +60,12 @@ internal void PostEvent<T>(T ev) where T : EventBase => Task.Run(async () =>
4760
4861 try
4962 {
50- if ( _syncHandlers . TryGetValue ( typeof ( T ) , out var @delegate ) )
63+ if ( _syncHandlers . TryGetValue ( typeof ( T ) , out var @delegate ) && @delegate is not null )
5164 {
5265 ( ( Action < BotContext , EventBase > ) @delegate ) . Invoke ( context , ev ) ;
5366 }
5467
55- if ( _asyncHandlers . TryGetValue ( typeof ( T ) , out var asyncDelegate ) )
68+ if ( _asyncHandlers . TryGetValue ( typeof ( T ) , out var asyncDelegate ) && asyncDelegate is not null )
5669 {
5770 await ( ( Func < BotContext , EventBase , Task > ) asyncDelegate ) ( context , ev ) ;
5871 }
@@ -74,55 +87,66 @@ public void Dispose()
7487 _asyncHandlers . Clear ( ) ;
7588 }
7689
77- private static void AddHandler ( ConcurrentDictionary < Type , Delegate > dict , Type key , Delegate handler )
90+ private static void AddHandler ( ConcurrentDictionary < Type , Delegate ? > dict , Type key , Delegate handler )
7891 {
7992 dict . AddOrUpdate ( key , handler , ( _ , old ) => Delegate . Combine ( old , handler ) ) ;
8093 }
8194
82- private static void RemoveHandler ( ConcurrentDictionary < Type , Delegate > dict , Type key , Delegate handler )
95+ private static void RemoveHandler ( ConcurrentDictionary < Type , Delegate ? > dict , Type key , Delegate handler )
8396 {
84- dict . AddOrUpdate ( key , _ => null ! , ( _ , old ) =>
85- {
86- var updated = Delegate . Remove ( old , handler ) ;
87- return updated != null && updated . GetInvocationList ( ) . Length > 0 ? updated : null ! ;
88- } ) ;
97+ dict . AddOrUpdate ( key , _ => null , ( _ , old ) => Delegate . Remove ( old , handler ) ! ) ;
8998
90- if ( dict . TryRemove ( key , out var d ) && d is not { } ) dict . TryRemove ( key , out _ ) ; // is not { } is needed to avoid removing the key if the delegate is null, and escape from ReSharper's warning
99+ if ( dict . TryGetValue ( key , out var currentDelegate ) && currentDelegate == null )
100+ {
101+ dict . TryRemove ( key , out _ ) ;
102+ }
91103 }
92104
93105 [ UnconditionalSuppressMessage ( "AOT" , "IL3050" , Justification = "DynamicMethod is not supported in AOT, but this branch is skipped there" ) ]
94- private static Action < BotContext , EventBase > BuildSyncDelegate < TEvent > ( LagrangeEventHandler < TEvent > h ) where TEvent : EventBase
106+ private static Delegate BuildSyncDelegate < TEvent > ( LagrangeEventHandler < TEvent > h ) where TEvent : EventBase
95107 {
96- if ( ! RuntimeFeature . IsDynamicCodeSupported ) return ( ctx , ev ) => h ( ctx , ( TEvent ) ev ) ;
108+ return SyncHandlerCache . GetOrAdd ( h , handler =>
109+ {
110+ if ( ! RuntimeFeature . IsDynamicCodeSupported )
111+ {
112+ return ( Action < BotContext , EventBase > ) ( ( ctx , ev ) => ( ( LagrangeEventHandler < TEvent > ) handler ) ( ctx , ( TEvent ) ev ) ) ;
113+ }
97114
98- var dm = new DynamicMethod ( $ "EventInvoker_{ typeof ( TEvent ) . Name } ", typeof ( void ) , [ h . Target ! . GetType ( ) , typeof ( BotContext ) , typeof ( EventBase ) ] , typeof ( EventInvoker ) , true ) ;
99- var il = dm . GetILGenerator ( ) ;
100-
101- il . Emit ( OpCodes . Ldarg_0 ) ;
102- il . Emit ( OpCodes . Ldarg_1 ) ;
103- il . Emit ( OpCodes . Ldarg_2 ) ;
104- il . Emit ( OpCodes . Castclass , typeof ( TEvent ) ) ;
105- il . Emit ( OpCodes . Callvirt , h . Method ) ;
106- il . Emit ( OpCodes . Ret ) ;
107-
108- return dm . CreateDelegate < Action < BotContext , EventBase > > ( h . Target ) ;
115+ var dm = new DynamicMethod ( $ "EventInvoker_{ typeof ( TEvent ) . Name } _{ Guid . NewGuid ( ) } ", typeof ( void ) , [ handler . Target ! . GetType ( ) , typeof ( BotContext ) , typeof ( EventBase ) ] , typeof ( EventInvoker ) , true ) ;
116+ var il = dm . GetILGenerator ( ) ;
117+
118+ il . Emit ( OpCodes . Ldarg_0 ) ;
119+ il . Emit ( OpCodes . Ldarg_1 ) ;
120+ il . Emit ( OpCodes . Ldarg_2 ) ;
121+ il . Emit ( OpCodes . Castclass , typeof ( TEvent ) ) ;
122+ il . Emit ( OpCodes . Callvirt , handler . Method ) ;
123+ il . Emit ( OpCodes . Ret ) ;
124+
125+ return dm . CreateDelegate < Action < BotContext , EventBase > > ( handler . Target ) ;
126+ } ) ;
109127 }
110128
111129 [ UnconditionalSuppressMessage ( "AOT" , "IL3050" , Justification = "DynamicMethod is not supported in AOT, but this branch is skipped there" ) ]
112- private static Func < BotContext , EventBase , Task > BuildAsyncDelegate < TEvent > ( LagrangeAsyncEventHandler < TEvent > h ) where TEvent : EventBase
130+ private static Delegate BuildAsyncDelegate < TEvent > ( LagrangeAsyncEventHandler < TEvent > h ) where TEvent : EventBase
113131 {
114- if ( ! RuntimeFeature . IsDynamicCodeSupported ) return ( ctx , ev ) => h ( ctx , ( TEvent ) ev ) ;
132+ return AsyncHandlerCache . GetOrAdd ( h , handler =>
133+ {
134+ if ( ! RuntimeFeature . IsDynamicCodeSupported )
135+ {
136+ return ( Func < BotContext , EventBase , Task > ) ( ( ctx , ev ) => ( ( LagrangeAsyncEventHandler < TEvent > ) handler ) ( ctx , ( TEvent ) ev ) ) ;
137+ }
115138
116- var dm = new DynamicMethod ( $ "EventInvoker_{ typeof ( TEvent ) . Name } _Async", typeof ( Task ) , [ h . Target ! . GetType ( ) , typeof ( BotContext ) , typeof ( EventBase ) ] , typeof ( EventInvoker ) , true ) ;
117- var il = dm . GetILGenerator ( ) ;
118-
119- il . Emit ( OpCodes . Ldarg_0 ) ;
120- il . Emit ( OpCodes . Ldarg_1 ) ;
121- il . Emit ( OpCodes . Ldarg_2 ) ;
122- il . Emit ( OpCodes . Castclass , typeof ( TEvent ) ) ;
123- il . Emit ( OpCodes . Callvirt , h . Method ) ;
124- il . Emit ( OpCodes . Ret ) ;
125-
126- return dm . CreateDelegate < Func < BotContext , EventBase , Task > > ( h . Target ) ;
139+ var dm = new DynamicMethod ( $ "EventInvoker_{ typeof ( TEvent ) . Name } _Async_{ Guid . NewGuid ( ) } ", typeof ( Task ) , [ handler . Target ! . GetType ( ) , typeof ( BotContext ) , typeof ( EventBase ) ] , typeof ( EventInvoker ) , true ) ;
140+ var il = dm . GetILGenerator ( ) ;
141+
142+ il . Emit ( OpCodes . Ldarg_0 ) ;
143+ il . Emit ( OpCodes . Ldarg_1 ) ;
144+ il . Emit ( OpCodes . Ldarg_2 ) ;
145+ il . Emit ( OpCodes . Castclass , typeof ( TEvent ) ) ;
146+ il . Emit ( OpCodes . Callvirt , handler . Method ) ;
147+ il . Emit ( OpCodes . Ret ) ;
148+
149+ return dm . CreateDelegate < Func < BotContext , EventBase , Task > > ( handler . Target ) ;
150+ } ) ;
127151 }
128- }
152+ }
0 commit comments