Skip to content

Commit b50a8fd

Browse files
authored
Fixes #4162. Keyword dynamic isn't AOT-Compatible and must be removed (#4163)
1 parent 6e486c7 commit b50a8fd

File tree

4 files changed

+106
-32
lines changed

4 files changed

+106
-32
lines changed

Examples/UICatalog/Scenarios/NumericUpDownDemo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ void IncrementOnAccept (object? o, EventArgs eventArgs)
252252
{
253253
X = Pos.Center (),
254254
Y = Pos.Bottom (_increment) + 1,
255-
Increment = (dynamic)1,
255+
Increment = NumericUpDown<int>.TryConvert (1, out T? increment) ? increment : default,
256256
};
257257

258258
_numericUpDown.ValueChanged += NumericUpDownOnValueChanged;

Examples/UICatalog/Scenarios/TextAlignmentAndDirection.cs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ public override void Main ()
410410
// Save Alignment in Data
411411
foreach (View t in multiLineLabels)
412412
{
413-
t.Data = new { h = t.TextAlignment, v = t.VerticalTextAlignment };
413+
t.Data = new TextAlignmentData (t.TextAlignment, t.VerticalTextAlignment);
414414
}
415415

416416
container.Add (txtLabelTL);
@@ -594,8 +594,9 @@ void ToggleJustify (bool oldValue, bool wasJustOptions = false)
594594

595595
foreach (View t in multiLineLabels)
596596
{
597-
t.TextAlignment = (Alignment)((dynamic)t.Data).h;
598-
t.VerticalTextAlignment = (Alignment)((dynamic)t.Data).v;
597+
var data = (TextAlignmentData)t.Data;
598+
t.TextAlignment = data!.h;
599+
t.VerticalTextAlignment = data.v;
599600
}
600601
}
601602
else
@@ -607,24 +608,23 @@ void ToggleJustify (bool oldValue, bool wasJustOptions = false)
607608
justifyOptions.Enabled = true;
608609
}
609610

611+
var data = (TextAlignmentData)t.Data;
612+
610613
if (TextFormatter.IsVerticalDirection (t.TextDirection))
611614
{
612615
switch (justifyOptions.SelectedItem)
613616
{
614617
case 0:
615618
t.VerticalTextAlignment = Alignment.Fill;
616-
t.TextAlignment = ((dynamic)t.Data).h;
617-
619+
t.TextAlignment = data!.h;
618620
break;
619621
case 1:
620-
t.VerticalTextAlignment = (Alignment)((dynamic)t.Data).v;
622+
t.VerticalTextAlignment = data!.v;
621623
t.TextAlignment = Alignment.Fill;
622-
623624
break;
624625
case 2:
625626
t.VerticalTextAlignment = Alignment.Fill;
626627
t.TextAlignment = Alignment.Fill;
627-
628628
break;
629629
}
630630
}
@@ -634,23 +634,26 @@ void ToggleJustify (bool oldValue, bool wasJustOptions = false)
634634
{
635635
case 0:
636636
t.TextAlignment = Alignment.Fill;
637-
t.VerticalTextAlignment = ((dynamic)t.Data).v;
638-
637+
t.VerticalTextAlignment = data!.v;
639638
break;
640639
case 1:
641-
t.TextAlignment = (Alignment)((dynamic)t.Data).h;
640+
t.TextAlignment = data!.h;
642641
t.VerticalTextAlignment = Alignment.Fill;
643-
644642
break;
645643
case 2:
646644
t.TextAlignment = Alignment.Fill;
647645
t.VerticalTextAlignment = Alignment.Fill;
648-
649646
break;
650647
}
651648
}
652649
}
653650
}
654651
}
655652
}
653+
654+
private class TextAlignmentData (Alignment h, Alignment v)
655+
{
656+
public Alignment h { get; } = h;
657+
public Alignment v { get; } = v;
658+
}
656659
}

Terminal.Gui/Views/NumericUpDown.cs

Lines changed: 82 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22
using System.ComponentModel;
3+
using System.Numerics;
34

45
namespace Terminal.Gui.Views;
56

@@ -26,22 +27,19 @@ public NumericUpDown ()
2627
{
2728
Type type = typeof (T);
2829

29-
if (!(type == typeof (object)
30-
|| type == typeof (int)
31-
|| type == typeof (long)
32-
|| type == typeof (double)
33-
|| type == typeof (float)
34-
|| type == typeof (double)
35-
|| type == typeof (decimal)))
30+
if (!(type == typeof (object) || NumericHelper.SupportsType (type)))
3631
{
3732
throw new InvalidOperationException ("T must be a numeric type that supports addition and subtraction.");
3833
}
3934

4035
// `object` is supported only for AllViewsTester
4136
if (type != typeof (object))
4237
{
43-
Increment = (dynamic)1;
44-
Value = (dynamic)0;
38+
if (NumericHelper.TryGetHelper (typeof (T), out INumericHelper? helper))
39+
{
40+
Increment = (T)helper!.One;
41+
Value = (T)helper!.Zero;
42+
}
4543
}
4644

4745
Width = Dim.Auto (DimAutoStyle.Content);
@@ -106,11 +104,10 @@ public NumericUpDown ()
106104
// return true;
107105
//}
108106

109-
if (Value is { } && Increment is { })
107+
if (Value is { } v && Increment is { } i && NumericHelper.TryGetHelper (typeof (T), out INumericHelper? helper))
110108
{
111-
Value = (dynamic)Value + (dynamic)Increment;
109+
Value = (T)helper!.Add (v, i);
112110
}
113-
114111
return true;
115112
});
116113

@@ -129,12 +126,10 @@ public NumericUpDown ()
129126
// return true;
130127
//}
131128

132-
if (Value is { } && Increment is { })
129+
if (Value is { } v && Increment is { } i && NumericHelper.TryGetHelper (typeof (T), out INumericHelper? helper))
133130
{
134-
Value = (dynamic)Value - (dynamic)Increment;
131+
Value = (T)helper!.Subtract (v, i);
135132
}
136-
137-
138133
return true;
139134
});
140135

@@ -248,7 +243,7 @@ public T? Increment
248243
get => _increment;
249244
set
250245
{
251-
if (_increment is { } && value is { } && (dynamic)_increment == (dynamic)value)
246+
if (_increment is { } oldVal && value is { } newVal && oldVal.Equals (newVal))
252247
{
253248
return;
254249
}
@@ -267,10 +262,80 @@ public T? Increment
267262
// Prevent the drawing of Text
268263
/// <inheritdoc />
269264
protected override bool OnDrawingText () { return true; }
265+
266+
/// <summary>
267+
/// Attempts to convert the specified <paramref name="value"/> to type <typeparamref name="T"/>.
268+
/// </summary>
269+
/// <typeparam name="T">The type to which the value should be converted.</typeparam>
270+
/// <param name="value">The value to convert.</param>
271+
/// <param name="result">
272+
/// When this method returns, contains the converted value if the conversion succeeded,
273+
/// or the default value of <typeparamref name="T"/> if the conversion failed.
274+
/// </param>
275+
/// <returns>
276+
/// <c>true</c> if the conversion was successful; otherwise, <c>false</c>.
277+
/// </returns>
278+
public static bool TryConvert<T> (object value, out T? result)
279+
{
280+
try
281+
{
282+
result = (T)Convert.ChangeType (value, typeof (T));
283+
284+
return true;
285+
}
286+
catch
287+
{
288+
result = default (T);
289+
290+
return false;
291+
}
292+
}
270293
}
271294

272295
/// <summary>
273296
/// Enables the user to increase or decrease an <see langword="int"/> by clicking on the up or down buttons.
274297
/// </summary>
275298
public class NumericUpDown : NumericUpDown<int>
276299
{ }
300+
301+
internal interface INumericHelper
302+
{
303+
object One { get; }
304+
object Zero { get; }
305+
object Add (object a, object b);
306+
object Subtract (object a, object b);
307+
}
308+
309+
internal static class NumericHelper
310+
{
311+
private static readonly Dictionary<Type, INumericHelper> _helpers = new ();
312+
313+
static NumericHelper ()
314+
{
315+
// Register known INumber<T> types
316+
Register<int> ();
317+
Register<long> ();
318+
Register<float> ();
319+
Register<double> ();
320+
Register<decimal> ();
321+
// Add more as needed
322+
}
323+
324+
private static void Register<T> () where T : INumber<T>
325+
{
326+
_helpers [typeof (T)] = new NumericHelperImpl<T> ();
327+
}
328+
329+
public static bool TryGetHelper (Type t, out INumericHelper? helper)
330+
=> _helpers.TryGetValue (t, out helper);
331+
332+
private class NumericHelperImpl<T> : INumericHelper where T : INumber<T>
333+
{
334+
public object One => T.One;
335+
public object Zero => T.Zero;
336+
public object Add (object a, object b) => (T)a + (T)b;
337+
public object Subtract (object a, object b) => (T)a - (T)b;
338+
}
339+
340+
public static bool SupportsType (Type type) => _helpers.ContainsKey (type);
341+
}

Tests/UnitTestsParallelizable/Views/NumericUpDownTests.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using System.Globalization;
2-
using Xunit.Abstractions;
32

43
namespace Terminal.Gui.ViewsTests;
54

@@ -102,6 +101,13 @@ public void WhenCreatedWithInvalidTypeObject_ShouldNotThrowInvalidOperationExcep
102101
Assert.Null (exception);
103102
}
104103

104+
[Fact]
105+
public void WhenCreatedWithValidNumberType_ShouldThrowInvalidOperationException_UnlessTheyAreRegisterAsValid ()
106+
{
107+
Exception exception = Record.Exception (() => new NumericUpDown<short> ());
108+
Assert.NotNull (exception);
109+
}
110+
105111
[Fact]
106112
public void WhenCreated_ShouldHaveDefaultWidthAndHeight_int ()
107113
{

0 commit comments

Comments
 (0)