Преглед изворни кода

Merge pull request #8787 from hez2010/master

Remove most reflection usage on animators
Nikita Tsukanov пре 3 година
родитељ
комит
b9cc4377e8

+ 35 - 28
src/Avalonia.Base/Animation/Animation.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
 using System.Linq;
 using System.Reactive.Disposables;
 using System.Reactive.Linq;
@@ -179,14 +180,14 @@ namespace Avalonia.Animation
         public KeyFrames Children { get; } = new KeyFrames();
 
         // Store values for the Animator attached properties for IAnimationSetter objects.
-        private static readonly Dictionary<IAnimationSetter, Type> s_animators = new Dictionary<IAnimationSetter, Type>();
+        private static readonly Dictionary<IAnimationSetter, (Type Type, Func<IAnimator> Factory)> s_animators = new();
 
         /// <summary>
         /// Gets the value of the Animator attached property for a setter.
         /// </summary>
         /// <param name="setter">The animation setter.</param>
         /// <returns>The property animator type.</returns>
-        public static Type? GetAnimator(IAnimationSetter setter)
+        public static (Type Type, Func<IAnimator> Factory)? GetAnimator(IAnimationSetter setter)
         {
             if (s_animators.TryGetValue(setter, out var type))
             {
@@ -200,24 +201,28 @@ namespace Avalonia.Animation
         /// </summary>
         /// <param name="setter">The animation setter.</param>
         /// <param name="value">The property animator value.</param>
-        public static void SetAnimator(IAnimationSetter setter, Type value)
+        public static void SetAnimator(IAnimationSetter setter, 
+#if NET6_0_OR_GREATER
+            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)]
+#endif
+            Type value)
         {
-            s_animators[setter] = value;
+            s_animators[setter] = (value, () => (IAnimator)Activator.CreateInstance(value)!);
         }
 
-        private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator)> Animators = new List<(Func<AvaloniaProperty, bool>, Type)>
+        private readonly static List<(Func<AvaloniaProperty, bool> Condition, Type Animator, Func<IAnimator> Factory)> Animators = new()
         {
-            ( prop => typeof(bool).IsAssignableFrom(prop.PropertyType), typeof(BoolAnimator) ),
-            ( prop => typeof(byte).IsAssignableFrom(prop.PropertyType), typeof(ByteAnimator) ),
-            ( prop => typeof(Int16).IsAssignableFrom(prop.PropertyType), typeof(Int16Animator) ),
-            ( prop => typeof(Int32).IsAssignableFrom(prop.PropertyType), typeof(Int32Animator) ),
-            ( prop => typeof(Int64).IsAssignableFrom(prop.PropertyType), typeof(Int64Animator) ),
-            ( prop => typeof(UInt16).IsAssignableFrom(prop.PropertyType), typeof(UInt16Animator) ),
-            ( prop => typeof(UInt32).IsAssignableFrom(prop.PropertyType), typeof(UInt32Animator) ),
-            ( prop => typeof(UInt64).IsAssignableFrom(prop.PropertyType), typeof(UInt64Animator) ),
-            ( prop => typeof(float).IsAssignableFrom(prop.PropertyType), typeof(FloatAnimator) ),
-            ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator) ),
-            ( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator) ),
+            ( prop => typeof(bool).IsAssignableFrom(prop.PropertyType), typeof(BoolAnimator), () => new BoolAnimator() ),
+            ( prop => typeof(byte).IsAssignableFrom(prop.PropertyType), typeof(ByteAnimator), () => new ByteAnimator() ),
+            ( prop => typeof(Int16).IsAssignableFrom(prop.PropertyType), typeof(Int16Animator), () => new Int16Animator() ),
+            ( prop => typeof(Int32).IsAssignableFrom(prop.PropertyType), typeof(Int32Animator), () => new Int32Animator() ),
+            ( prop => typeof(Int64).IsAssignableFrom(prop.PropertyType), typeof(Int64Animator), () => new Int64Animator() ),
+            ( prop => typeof(UInt16).IsAssignableFrom(prop.PropertyType), typeof(UInt16Animator), () => new UInt16Animator() ),
+            ( prop => typeof(UInt32).IsAssignableFrom(prop.PropertyType), typeof(UInt32Animator), () => new UInt32Animator() ),
+            ( prop => typeof(UInt64).IsAssignableFrom(prop.PropertyType), typeof(UInt64Animator), () => new UInt64Animator() ),
+            ( prop => typeof(float).IsAssignableFrom(prop.PropertyType), typeof(FloatAnimator), () => new FloatAnimator() ),
+            ( prop => typeof(double).IsAssignableFrom(prop.PropertyType), typeof(DoubleAnimator), () => new DoubleAnimator() ),
+            ( prop => typeof(decimal).IsAssignableFrom(prop.PropertyType), typeof(DecimalAnimator), () => new DecimalAnimator() ),
         };
 
         /// <summary>
@@ -232,18 +237,18 @@ namespace Avalonia.Animation
         /// The type of the animator to instantiate.
         /// </typeparam>
         public static void RegisterAnimator<TAnimator>(Func<AvaloniaProperty, bool> condition)
-            where TAnimator : IAnimator
+            where TAnimator : IAnimator, new()
         {
-            Animators.Insert(0, (condition, typeof(TAnimator)));
+            Animators.Insert(0, (condition, typeof(TAnimator), () => new TAnimator()));
         }
 
-        private static Type? GetAnimatorType(AvaloniaProperty property)
+        private static (Type Type, Func<IAnimator> Factory)? GetAnimatorType(AvaloniaProperty property)
         {
-            foreach (var (condition, type) in Animators)
+            foreach (var (condition, type, factory) in Animators)
             {
                 if (condition(property))
                 {
-                    return type;
+                    return (type, factory);
                 }
             }
             return null;
@@ -251,7 +256,7 @@ namespace Avalonia.Animation
 
         private (IList<IAnimator> Animators, IList<IDisposable> subscriptions) InterpretKeyframes(Animatable control)
         {
-            var handlerList = new List<(Type type, AvaloniaProperty property)>();
+            var handlerList = new Dictionary<(Type type, AvaloniaProperty Property), Func<IAnimator>>();
             var animatorKeyFrames = new List<AnimatorKeyFrame>();
             var subscriptions = new List<IDisposable>();
 
@@ -271,8 +276,10 @@ namespace Avalonia.Animation
                         throw new InvalidOperationException($"No animator registered for the property {setter.Property}. Add an animator to the Animation.Animators collection that matches this property to animate it.");
                     }
 
-                    if (!handlerList.Contains((handler, setter.Property)))
-                        handlerList.Add((handler, setter.Property));
+                    var (type, factory) = handler.Value;
+
+                    if (!handlerList.ContainsKey((type, setter.Property)))
+                        handlerList[(type, setter.Property)] = factory;
 
                     var cue = keyframe.Cue;
 
@@ -281,7 +288,7 @@ namespace Avalonia.Animation
                         cue = new Cue(keyframe.KeyTime.TotalSeconds / Duration.TotalSeconds);
                     }
 
-                    var newKF = new AnimatorKeyFrame(handler, cue, keyframe.KeySpline);
+                    var newKF = new AnimatorKeyFrame(type, factory, cue, keyframe.KeySpline);
 
                     subscriptions.Add(newKF.BindSetter(setter, control));
 
@@ -291,10 +298,10 @@ namespace Avalonia.Animation
 
             var newAnimatorInstances = new List<IAnimator>();
 
-            foreach (var (handlerType, property) in handlerList)
+            foreach (var handler in handlerList)
             {
-                var newInstance = (IAnimator)Activator.CreateInstance(handlerType)!;
-                newInstance.Property = property;
+                var newInstance = handler.Value();
+                newInstance.Property = handler.Key.Property;
                 newAnimatorInstances.Add(newInstance);
             }
 

+ 5 - 2
src/Avalonia.Base/Animation/AnimatorKeyFrame.cs

@@ -20,22 +20,25 @@ namespace Avalonia.Animation
 
         }
 
-        public AnimatorKeyFrame(Type? animatorType, Cue cue)
+        public AnimatorKeyFrame(Type? animatorType, Func<IAnimator>? animatorFactory, Cue cue)
         {
             AnimatorType = animatorType;
+            AnimatorFactory = animatorFactory;
             Cue = cue;
             KeySpline = null;
         }
 
-        public AnimatorKeyFrame(Type? animatorType, Cue cue, KeySpline? keySpline)
+        public AnimatorKeyFrame(Type? animatorType, Func<IAnimator>? animatorFactory, Cue cue, KeySpline? keySpline)
         {
             AnimatorType = animatorType;
+            AnimatorFactory = animatorFactory;
             Cue = cue;
             KeySpline = keySpline;
         }
 
         internal bool isNeutral;
         public Type? AnimatorType { get; }
+        public Func<IAnimator>? AnimatorFactory { get; }
         public Cue Cue { get; }
         public KeySpline? KeySpline { get; }
         public AvaloniaProperty? Property { get; private set; }

+ 2 - 2
src/Avalonia.Base/Animation/Animators/Animator`1.cs

@@ -171,12 +171,12 @@ namespace Avalonia.Animation.Animators
         {
             if (!hasStartKey)
             {
-                _convertedKeyframes.Insert(0, new AnimatorKeyFrame(null, new Cue(0.0d)) { Value = default(T), isNeutral = true });
+                _convertedKeyframes.Insert(0, new AnimatorKeyFrame(null, null, new Cue(0.0d)) { Value = default(T), isNeutral = true });
             }
 
             if (!hasEndKey)
             {
-                _convertedKeyframes.Add(new AnimatorKeyFrame(null, new Cue(1.0d)) { Value = default(T), isNeutral = true });
+                _convertedKeyframes.Add(new AnimatorKeyFrame(null, null, new Cue(1.0d)) { Value = default(T), isNeutral = true });
             }
         }
     }

+ 8 - 9
src/Avalonia.Base/Animation/Animators/BaseBrushAnimator.cs

@@ -17,8 +17,7 @@ namespace Avalonia.Animation.Animators
     /// </summary>
     public class BaseBrushAnimator : Animator<IBrush?>
     {
-        private static readonly List<(Func<Type, bool> Match, Type AnimatorType)> _brushAnimators =
-            new List<(Func<Type, bool> Match, Type AnimatorType)>();
+        private static readonly List<(Func<Type, bool> Match, Type AnimatorType, Func<IAnimator> AnimatorFactory)> _brushAnimators = new();
 
         /// <summary>
         /// Register an <see cref="Animator{T}"/> that handles a specific
@@ -34,7 +33,7 @@ namespace Avalonia.Animation.Animators
         public static void RegisterBrushAnimator<TAnimator>(Func<Type, bool> condition)
             where TAnimator : IAnimator, new()
         {
-            _brushAnimators.Insert(0, (condition, typeof(TAnimator)));
+            _brushAnimators.Insert(0, (condition, typeof(TAnimator), () => new TAnimator()));
         }
 
         /// <inheritdoc/>
@@ -85,14 +84,14 @@ namespace Avalonia.Animation.Animators
             {
                 if (keyframe.Value is ISolidColorBrush solidColorBrush)
                 {
-                    gradientAnimator.Add(new AnimatorKeyFrame(typeof(GradientBrushAnimator), keyframe.Cue, keyframe.KeySpline)
+                    gradientAnimator.Add(new AnimatorKeyFrame(typeof(GradientBrushAnimator), () => new GradientBrushAnimator(), keyframe.Cue, keyframe.KeySpline)
                     {
                         Value = GradientBrushAnimator.ConvertSolidColorBrushToGradient(firstGradient, solidColorBrush)
                     });
                 }
                 else if (keyframe.Value is IGradientBrush)
                 {
-                    gradientAnimator.Add(new AnimatorKeyFrame(typeof(GradientBrushAnimator), keyframe.Cue, keyframe.KeySpline)
+                    gradientAnimator.Add(new AnimatorKeyFrame(typeof(GradientBrushAnimator), () => new GradientBrushAnimator(), keyframe.Cue, keyframe.KeySpline)
                     {
                         Value = keyframe.Value
                     });
@@ -117,7 +116,7 @@ namespace Avalonia.Animation.Animators
             {
                 if (keyframe.Value is ISolidColorBrush)
                 {
-                    solidColorBrushAnimator.Add(new AnimatorKeyFrame(typeof(ISolidColorBrushAnimator), keyframe.Cue, keyframe.KeySpline)
+                    solidColorBrushAnimator.Add(new AnimatorKeyFrame(typeof(ISolidColorBrushAnimator), () => new ISolidColorBrushAnimator(), keyframe.Cue, keyframe.KeySpline)
                     {
                         Value = keyframe.Value
                     });
@@ -137,18 +136,18 @@ namespace Avalonia.Animation.Animators
         {
             if (_brushAnimators.Count > 0 && this[0].Value?.GetType() is Type firstKeyType)
             {
-                foreach (var (match, animatorType) in _brushAnimators)
+                foreach (var (match, animatorType, animatorFactory) in _brushAnimators)
                 {
                     if (!match(firstKeyType))
                         continue;
 
-                    animator = (IAnimator?)Activator.CreateInstance(animatorType);
+                    animator = animatorFactory();
                     if (animator != null)
                     {
                         animator.Property = Property;
                         foreach (var keyframe in this)
                         {
-                            animator.Add(new AnimatorKeyFrame(animatorType, keyframe.Cue, keyframe.KeySpline)
+                            animator.Add(new AnimatorKeyFrame(animatorType, animatorFactory, keyframe.Cue, keyframe.KeySpline)
                             {
                                 Value = keyframe.Value
                             });

+ 1 - 0
src/Avalonia.Base/Avalonia.Base.csproj

@@ -6,6 +6,7 @@
     <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
     <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
     <CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GeneratedFiles</CompilerGeneratedFilesOutputPath>
+    <IsTrimmable>true</IsTrimmable>
   </PropertyGroup>
   <ItemGroup>
     <EmbeddedResource Include="Assets\*.trie" />