Ionite
11 months ago
2 changed files with 90 additions and 0 deletions
@ -0,0 +1,84 @@ |
|||||||
|
using System; |
||||||
|
using System.ComponentModel; |
||||||
|
using System.Diagnostics.CodeAnalysis; |
||||||
|
using System.Globalization; |
||||||
|
using System.Numerics; |
||||||
|
using Avalonia.Data.Converters; |
||||||
|
|
||||||
|
namespace StabilityMatrix.Avalonia.Converters; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Converts a possibly boxed nullable value type to its default value |
||||||
|
/// </summary> |
||||||
|
public class NullableDefaultNumericConverter<TSource, TTarget> : IValueConverter |
||||||
|
where TSource : unmanaged, INumber<TSource> |
||||||
|
where TTarget : unmanaged, INumber<TTarget> |
||||||
|
{ |
||||||
|
public ReturnBehavior NanHandling { get; set; } = ReturnBehavior.DefaultValue; |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Unboxes a nullable value type |
||||||
|
/// </summary> |
||||||
|
private TSource Unbox(TTarget? value) |
||||||
|
{ |
||||||
|
if (!value.HasValue) |
||||||
|
{ |
||||||
|
return default; |
||||||
|
} |
||||||
|
|
||||||
|
if (TTarget.IsNaN(value.Value)) |
||||||
|
{ |
||||||
|
return NanHandling switch |
||||||
|
{ |
||||||
|
ReturnBehavior.DefaultValue => default, |
||||||
|
ReturnBehavior.Throw => throw new InvalidCastException("Cannot convert NaN to a numeric type"), |
||||||
|
_ |
||||||
|
=> throw new InvalidEnumArgumentException( |
||||||
|
nameof(NanHandling), |
||||||
|
(int)NanHandling, |
||||||
|
typeof(ReturnBehavior) |
||||||
|
) |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
return (TSource)System.Convert.ChangeType(value.Value, typeof(TSource)); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Convert a value type to a nullable value type |
||||||
|
/// </summary> |
||||||
|
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture) |
||||||
|
{ |
||||||
|
if (targetType != typeof(TTarget?) && !targetType.IsAssignableTo(typeof(TTarget))) |
||||||
|
{ |
||||||
|
// ReSharper disable once LocalizableElement |
||||||
|
throw new ArgumentException( |
||||||
|
$"Convert Target type {targetType.Name} must be assignable to {typeof(TTarget).Name}" |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return (TTarget?)System.Convert.ChangeType(value, typeof(TTarget)); |
||||||
|
} |
||||||
|
|
||||||
|
/// <summary> |
||||||
|
/// Convert a nullable value type to a value type |
||||||
|
/// </summary> |
||||||
|
public object ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) |
||||||
|
{ |
||||||
|
if (!targetType.IsAssignableTo(typeof(TSource))) |
||||||
|
{ |
||||||
|
// ReSharper disable once LocalizableElement |
||||||
|
throw new ArgumentException( |
||||||
|
$"ConvertBack Target type {targetType.Name} must be assignable to {typeof(TSource).Name}" |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
return Unbox((TTarget?)value); |
||||||
|
} |
||||||
|
|
||||||
|
public enum ReturnBehavior |
||||||
|
{ |
||||||
|
DefaultValue, |
||||||
|
Throw |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,6 @@ |
|||||||
|
namespace StabilityMatrix.Avalonia.Converters; |
||||||
|
|
||||||
|
public static class NullableDefaultNumericConverters |
||||||
|
{ |
||||||
|
public static readonly NullableDefaultNumericConverter<int, decimal> IntToDecimal = new(); |
||||||
|
} |
Loading…
Reference in new issue