Browse Source

Merge pull request #3621 from MarchingCube/optimize-event-handler-add-remove

Simplify routed event raise adapter and get rid of allocations when removing handlers.
pull/3607/head
Steven Kirk 7 years ago
committed by GitHub
parent
commit
5b6e9d2af1
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 35
      src/Avalonia.Interactivity/Interactive.cs

35
src/Avalonia.Interactivity/Interactive.cs

@ -15,9 +15,6 @@ namespace Avalonia.Interactivity
{ {
private Dictionary<RoutedEvent, List<EventSubscription>>? _eventHandlers; private Dictionary<RoutedEvent, List<EventSubscription>>? _eventHandlers;
private static readonly Dictionary<Type, Action<Delegate, object, RoutedEventArgs>> s_invokeHandlerCache
= new Dictionary<Type, Action<Delegate, object, RoutedEventArgs>>();
/// <summary> /// <summary>
/// Gets the interactive parent of the object for bubbling and tunneling events. /// Gets the interactive parent of the object for bubbling and tunneling events.
/// </summary> /// </summary>
@ -62,26 +59,16 @@ namespace Avalonia.Interactivity
routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent)); routedEvent = routedEvent ?? throw new ArgumentNullException(nameof(routedEvent));
handler = handler ?? throw new ArgumentNullException(nameof(handler)); handler = handler ?? throw new ArgumentNullException(nameof(handler));
// EventHandler delegate is not covariant, this forces us to create small wrapper static void InvokeAdapter(Delegate baseHandler, object sender, RoutedEventArgs args)
// that will cast our type erased instance and invoke it.
var eventArgsType = routedEvent.EventArgsType;
if (!s_invokeHandlerCache.TryGetValue(eventArgsType, out var invokeAdapter))
{ {
void InvokeAdapter(Delegate baseHandler, object sender, RoutedEventArgs args) var typedHandler = (EventHandler<TEventArgs>)baseHandler;
{ var typedArgs = (TEventArgs)args;
var typedHandler = (EventHandler<TEventArgs>)baseHandler;
var typedArgs = (TEventArgs)args;
typedHandler(sender, typedArgs);
}
invokeAdapter = InvokeAdapter;
s_invokeHandlerCache.Add(eventArgsType, invokeAdapter); typedHandler(sender, typedArgs);
} }
var subscription = new EventSubscription(handler, routes, handledEventsToo, invokeAdapter); var subscription = new EventSubscription(handler, routes, handledEventsToo, (baseHandler, sender, args) => InvokeAdapter(baseHandler, sender, args));
return AddEventSubscription(routedEvent, subscription); return AddEventSubscription(routedEvent, subscription);
} }
@ -96,9 +83,15 @@ namespace Avalonia.Interactivity
handler = handler ?? throw new ArgumentNullException(nameof(handler)); handler = handler ?? throw new ArgumentNullException(nameof(handler));
if (_eventHandlers is object && if (_eventHandlers is object &&
_eventHandlers.TryGetValue(routedEvent, out var subscriptions) == true) _eventHandlers.TryGetValue(routedEvent, out var subscriptions))
{ {
subscriptions.RemoveAll(x => x.Handler == handler); for (var i = subscriptions.Count - 1; i >= 0; i--)
{
if (subscriptions[i].Handler == handler)
{
subscriptions.RemoveAt(i);
}
}
} }
} }

Loading…
Cancel
Save