Pārlūkot izejas kodu

Merge pull request #3067 from MarchingCube/alloc-interactive-traversal

Optimize Interactive hierarchy traversal
Jumar Macato 6 gadi atpakaļ
vecāks
revīzija
378f3442f7

+ 69 - 8
src/Avalonia.Interactivity/Interactive.cs

@@ -4,6 +4,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Runtime.CompilerServices;
 using Avalonia.Layout;
 using Avalonia.VisualTree;
 
@@ -170,10 +171,9 @@ namespace Avalonia.Interactivity
 
             e.Route = RoutingStrategies.Bubble;
 
-            foreach (var target in this.GetBubbleEventRoute())
-            {
-                ((Interactive)target).RaiseEventImpl(e);
-            }
+            var traverser = HierarchyTraverser<RaiseEventTraverse, NopTraverse>.Create(e);
+
+            traverser.Traverse(this);
         }
 
         /// <summary>
@@ -186,10 +186,9 @@ namespace Avalonia.Interactivity
 
             e.Route = RoutingStrategies.Tunnel;
 
-            foreach (var target in this.GetTunnelEventRoute())
-            {
-                ((Interactive)target).RaiseEventImpl(e);
-            }
+            var traverser = HierarchyTraverser<NopTraverse, RaiseEventTraverse>.Create(e);
+
+            traverser.Traverse(this);
         }
 
         /// <summary>
@@ -262,5 +261,67 @@ namespace Avalonia.Interactivity
                 _subscriptions.Remove(_subscription);
             }
         }
+
+        private interface ITraverse
+        {
+            void Execute(IInteractive target, RoutedEventArgs e);
+        }
+
+        private struct NopTraverse : ITraverse
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            public void Execute(IInteractive target, RoutedEventArgs e)
+            {
+            }
+        }
+
+        private struct RaiseEventTraverse : ITraverse
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            public void Execute(IInteractive target, RoutedEventArgs e)
+            {
+                ((Interactive)target).RaiseEventImpl(e);
+            }
+        }
+
+        /// <summary>
+        /// Traverses interactive hierarchy allowing for raising events.
+        /// </summary>
+        /// <typeparam name="TPreTraverse">Called before parent is traversed.</typeparam>
+        /// <typeparam name="TPostTraverse">Called after parent has been traversed.</typeparam>
+        private struct HierarchyTraverser<TPreTraverse, TPostTraverse>
+            where TPreTraverse : struct, ITraverse
+            where TPostTraverse : struct, ITraverse
+        {
+            private TPreTraverse _preTraverse;
+            private TPostTraverse _postTraverse;
+            private readonly RoutedEventArgs _args;
+
+            private HierarchyTraverser(TPreTraverse preTraverse, TPostTraverse postTraverse, RoutedEventArgs args)
+            {
+                _preTraverse = preTraverse;
+                _postTraverse = postTraverse;
+                _args = args;
+            }
+
+            public static HierarchyTraverser<TPreTraverse, TPostTraverse> Create(RoutedEventArgs args)
+            {
+                return new HierarchyTraverser<TPreTraverse, TPostTraverse>(new TPreTraverse(), new TPostTraverse(), args);
+            }
+
+            public void Traverse(IInteractive target)
+            {
+                _preTraverse.Execute(target, _args);
+
+                IInteractive parent = target.InteractiveParent;
+
+                if (parent != null)
+                {
+                    Traverse(parent);
+                }
+
+                _postTraverse.Execute(target, _args);
+            }
+        }
     }
 }

+ 0 - 24
src/Avalonia.Interactivity/InteractiveExtensions.cs

@@ -36,29 +36,5 @@ namespace Avalonia.Interactivity
                 routes,
                 handledEventsToo));
         }
-
-        /// <summary>
-        /// Gets the route for bubbling events from the specified interactive.
-        /// </summary>
-        /// <param name="interactive">The interactive.</param>
-        /// <returns>The event route.</returns>
-        internal static IEnumerable<IInteractive> GetBubbleEventRoute(this IInteractive interactive)
-        {
-            while (interactive != null)
-            {
-                yield return interactive;
-                interactive = interactive.InteractiveParent;
-            }
-        }
-
-        /// <summary>
-        /// Gets the route for tunneling events from the specified interactive.
-        /// </summary>
-        /// <param name="interactive">The interactive.</param>
-        /// <returns>The event route.</returns>
-        internal static IEnumerable<IInteractive> GetTunnelEventRoute(this IInteractive interactive)
-        {
-            return interactive.GetBubbleEventRoute().Reverse();
-        }
     }
 }