Explorar el Código

Fixes #4162. Keyword dynamic isn't AOT-Compatible and must be removed (#4163)

BDisp hace 2 meses
padre
commit
b50a8fd665

+ 1 - 1
Examples/UICatalog/Scenarios/NumericUpDownDemo.cs

@@ -252,7 +252,7 @@ internal class NumericUpDownEditor<T> : View where T : notnull
             {
                 X = Pos.Center (),
                 Y = Pos.Bottom (_increment) + 1,
-                Increment = (dynamic)1,
+                Increment = NumericUpDown<int>.TryConvert (1, out T? increment) ? increment : default,
             };
 
             _numericUpDown.ValueChanged += NumericUpDownOnValueChanged;

+ 16 - 13
Examples/UICatalog/Scenarios/TextAlignmentAndDirection.cs

@@ -410,7 +410,7 @@ public class TextAlignmentAndDirection : Scenario
         // Save Alignment in Data
         foreach (View t in multiLineLabels)
         {
-            t.Data = new { h = t.TextAlignment, v = t.VerticalTextAlignment };
+            t.Data = new TextAlignmentData (t.TextAlignment, t.VerticalTextAlignment);
         }
 
         container.Add (txtLabelTL);
@@ -594,8 +594,9 @@ public class TextAlignmentAndDirection : Scenario
 
                 foreach (View t in multiLineLabels)
                 {
-                    t.TextAlignment = (Alignment)((dynamic)t.Data).h;
-                    t.VerticalTextAlignment = (Alignment)((dynamic)t.Data).v;
+                    var data = (TextAlignmentData)t.Data;
+                    t.TextAlignment = data!.h;
+                    t.VerticalTextAlignment = data.v;
                 }
             }
             else
@@ -607,24 +608,23 @@ public class TextAlignmentAndDirection : Scenario
                         justifyOptions.Enabled = true;
                     }
 
+                    var data = (TextAlignmentData)t.Data;
+
                     if (TextFormatter.IsVerticalDirection (t.TextDirection))
                     {
                         switch (justifyOptions.SelectedItem)
                         {
                             case 0:
                                 t.VerticalTextAlignment = Alignment.Fill;
-                                t.TextAlignment = ((dynamic)t.Data).h;
-
+                                t.TextAlignment = data!.h;
                                 break;
                             case 1:
-                                t.VerticalTextAlignment = (Alignment)((dynamic)t.Data).v;
+                                t.VerticalTextAlignment = data!.v;
                                 t.TextAlignment = Alignment.Fill;
-
                                 break;
                             case 2:
                                 t.VerticalTextAlignment = Alignment.Fill;
                                 t.TextAlignment = Alignment.Fill;
-
                                 break;
                         }
                     }
@@ -634,18 +634,15 @@ public class TextAlignmentAndDirection : Scenario
                         {
                             case 0:
                                 t.TextAlignment = Alignment.Fill;
-                                t.VerticalTextAlignment = ((dynamic)t.Data).v;
-
+                                t.VerticalTextAlignment = data!.v;
                                 break;
                             case 1:
-                                t.TextAlignment = (Alignment)((dynamic)t.Data).h;
+                                t.TextAlignment = data!.h;
                                 t.VerticalTextAlignment = Alignment.Fill;
-
                                 break;
                             case 2:
                                 t.TextAlignment = Alignment.Fill;
                                 t.VerticalTextAlignment = Alignment.Fill;
-
                                 break;
                         }
                     }
@@ -653,4 +650,10 @@ public class TextAlignmentAndDirection : Scenario
             }
         }
     }
+
+    private class TextAlignmentData (Alignment h, Alignment v)
+    {
+        public Alignment h { get; } = h;
+        public Alignment v { get; } = v;
+    }
 }

+ 82 - 17
Terminal.Gui/Views/NumericUpDown.cs

@@ -1,5 +1,6 @@
 #nullable enable
 using System.ComponentModel;
+using System.Numerics;
 
 namespace Terminal.Gui.Views;
 
@@ -26,13 +27,7 @@ public class NumericUpDown<T> : View where T : notnull
     {
         Type type = typeof (T);
 
-        if (!(type == typeof (object)
-              || type == typeof (int)
-              || type == typeof (long)
-              || type == typeof (double)
-              || type == typeof (float)
-              || type == typeof (double)
-              || type == typeof (decimal)))
+        if (!(type == typeof (object) || NumericHelper.SupportsType (type)))
         {
             throw new InvalidOperationException ("T must be a numeric type that supports addition and subtraction.");
         }
@@ -40,8 +35,11 @@ public class NumericUpDown<T> : View where T : notnull
         // `object` is supported only for AllViewsTester
         if (type != typeof (object))
         {
-            Increment = (dynamic)1;
-            Value = (dynamic)0;
+            if (NumericHelper.TryGetHelper (typeof (T), out INumericHelper? helper))
+            {
+                Increment = (T)helper!.One;
+                Value = (T)helper!.Zero;
+            }
         }
 
         Width = Dim.Auto (DimAutoStyle.Content);
@@ -106,11 +104,10 @@ public class NumericUpDown<T> : View where T : notnull
                         //    return true;
                         //}
 
-                        if (Value is { } && Increment is { })
+                        if (Value is { } v && Increment is { } i && NumericHelper.TryGetHelper (typeof (T), out INumericHelper? helper))
                         {
-                            Value = (dynamic)Value + (dynamic)Increment;
+                            Value = (T)helper!.Add (v, i);
                         }
-
                         return true;
                     });
 
@@ -129,12 +126,10 @@ public class NumericUpDown<T> : View where T : notnull
                         //    return true;
                         //}
 
-                        if (Value is { } && Increment is { })
+                        if (Value is { } v && Increment is { } i && NumericHelper.TryGetHelper (typeof (T), out INumericHelper? helper))
                         {
-                            Value = (dynamic)Value - (dynamic)Increment;
+                            Value = (T)helper!.Subtract (v, i);
                         }
-
-
                         return true;
                     });
 
@@ -248,7 +243,7 @@ public class NumericUpDown<T> : View where T : notnull
         get => _increment;
         set
         {
-            if (_increment is { } && value is { } && (dynamic)_increment == (dynamic)value)
+            if (_increment is { } oldVal && value is { } newVal && oldVal.Equals (newVal))
             {
                 return;
             }
@@ -267,6 +262,34 @@ public class NumericUpDown<T> : View where T : notnull
     // Prevent the drawing of Text
     /// <inheritdoc />
     protected override bool OnDrawingText () { return true; }
+
+    /// <summary>
+    ///     Attempts to convert the specified <paramref name="value"/> to type <typeparamref name="T"/>.
+    /// </summary>
+    /// <typeparam name="T">The type to which the value should be converted.</typeparam>
+    /// <param name="value">The value to convert.</param>
+    /// <param name="result">
+    ///     When this method returns, contains the converted value if the conversion succeeded,
+    ///     or the default value of <typeparamref name="T"/> if the conversion failed.
+    /// </param>
+    /// <returns>
+    ///     <c>true</c> if the conversion was successful; otherwise, <c>false</c>.
+    /// </returns>
+    public static bool TryConvert<T> (object value, out T? result)
+    {
+        try
+        {
+            result = (T)Convert.ChangeType (value, typeof (T));
+
+            return true;
+        }
+        catch
+        {
+            result = default (T);
+
+            return false;
+        }
+    }
 }
 
 /// <summary>
@@ -274,3 +297,45 @@ public class NumericUpDown<T> : View where T : notnull
 /// </summary>
 public class NumericUpDown : NumericUpDown<int>
 { }
+
+internal interface INumericHelper
+{
+    object One { get; }
+    object Zero { get; }
+    object Add (object a, object b);
+    object Subtract (object a, object b);
+}
+
+internal static class NumericHelper
+{
+    private static readonly Dictionary<Type, INumericHelper> _helpers = new ();
+
+    static NumericHelper ()
+    {
+        // Register known INumber<T> types
+        Register<int> ();
+        Register<long> ();
+        Register<float> ();
+        Register<double> ();
+        Register<decimal> ();
+        // Add more as needed
+    }
+
+    private static void Register<T> () where T : INumber<T>
+    {
+        _helpers [typeof (T)] = new NumericHelperImpl<T> ();
+    }
+
+    public static bool TryGetHelper (Type t, out INumericHelper? helper)
+        => _helpers.TryGetValue (t, out helper);
+
+    private class NumericHelperImpl<T> : INumericHelper where T : INumber<T>
+    {
+        public object One => T.One;
+        public object Zero => T.Zero;
+        public object Add (object a, object b) => (T)a + (T)b;
+        public object Subtract (object a, object b) => (T)a - (T)b;
+    }
+
+    public static bool SupportsType (Type type) => _helpers.ContainsKey (type);
+}

+ 7 - 1
Tests/UnitTestsParallelizable/Views/NumericUpDownTests.cs

@@ -1,5 +1,4 @@
 using System.Globalization;
-using Xunit.Abstractions;
 
 namespace Terminal.Gui.ViewsTests;
 
@@ -102,6 +101,13 @@ public class NumericUpDownTests
         Assert.Null (exception);
     }
 
+    [Fact]
+    public void WhenCreatedWithValidNumberType_ShouldThrowInvalidOperationException_UnlessTheyAreRegisterAsValid ()
+    {
+        Exception exception = Record.Exception (() => new NumericUpDown<short> ());
+        Assert.NotNull (exception);
+    }
+
     [Fact]
     public void WhenCreated_ShouldHaveDefaultWidthAndHeight_int ()
     {