From ed933b406fb190b9575ec2fd21c529939bab432c Mon Sep 17 00:00:00 2001 From: Sebastian Krysmanski Date: Mon, 18 Mar 2024 17:27:50 +0100 Subject: [PATCH] Expand numeric extension methods to nullable value types --- .../Extensions/TypeExtensions.cs | 48 ++++++++++++++++--- src/AppMotor.Core/Logging/LoggableValues.cs | 2 +- .../Tests/Extensions/TypeExtensionsTests.cs | 27 +++++++++-- 3 files changed, 67 insertions(+), 10 deletions(-) diff --git a/src/AppMotor.Core/Extensions/TypeExtensions.cs b/src/AppMotor.Core/Extensions/TypeExtensions.cs index 54a7445e..222d649c 100644 --- a/src/AppMotor.Core/Extensions/TypeExtensions.cs +++ b/src/AppMotor.Core/Extensions/TypeExtensions.cs @@ -21,12 +21,24 @@ public static class TypeExtensions /// and sbyte as well as . However, /// is excluded. /// + /// The type to check + /// Whether this method should return true for nullable + /// number types as well. If true, this method will return true for int?. + /// If false, it will return false in this case. /// /// [MustUseReturnValue] - public static bool IsNumericType(this Type type) + public static bool IsNumericType(this Type type, bool includeNullables) { - Validate.ArgumentWithName(nameof(type)).IsNotNull(type); + // Check if the type is a nullable value type and use the underlying type for the check. + if (includeNullables && type.IsValueType) + { + var underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType is not null) + { + return underlyingType.IsNumericType(includeNullables: false); + } + } if (type.IsPrimitive) { @@ -77,12 +89,24 @@ public static bool IsNumericType(this Type type) /// Returns whether this type is an integer type. This list includes byte, sbyte, , /// Int128, and UInt128. /// + /// The type to check + /// Whether this method should return true for nullable + /// number types as well. If true, this method will return true for int?. + /// If false, it will return false in this case. /// /// [MustUseReturnValue] - public static bool IsNumericIntegerType(this Type type) + public static bool IsNumericIntegerType(this Type type, bool includeNullables) { - Validate.ArgumentWithName(nameof(type)).IsNotNull(type); + // Check if the type is a nullable value type and use the underlying type for the check. + if (includeNullables && type.IsValueType) + { + var underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType is not null) + { + return underlyingType.IsNumericType(includeNullables: false); + } + } if (type.IsPrimitive) { @@ -125,12 +149,24 @@ public static bool IsNumericIntegerType(this Type type) /// Returns whether this type is a floating point type. This list includes float, /// double, decimal, and Half. /// + /// The type to check + /// Whether this method should return true for nullable + /// number types as well. If true, this method will return true for int?. + /// If false, it will return false in this case. /// /// [MustUseReturnValue] - public static bool IsNumericFloatType(this Type type) + public static bool IsNumericFloatType(this Type type, bool includeNullables) { - Validate.ArgumentWithName(nameof(type)).IsNotNull(type); + // Check if the type is a nullable value type and use the underlying type for the check. + if (includeNullables && type.IsValueType) + { + var underlyingType = Nullable.GetUnderlyingType(type); + if (underlyingType is not null) + { + return underlyingType.IsNumericType(includeNullables: false); + } + } if (type.IsPrimitive) { diff --git a/src/AppMotor.Core/Logging/LoggableValues.cs b/src/AppMotor.Core/Logging/LoggableValues.cs index a5fd16c4..628c9450 100644 --- a/src/AppMotor.Core/Logging/LoggableValues.cs +++ b/src/AppMotor.Core/Logging/LoggableValues.cs @@ -67,7 +67,7 @@ public static bool IsSimpleLoggableType(Type typeToCheck) typeToCheck = underlyingType; } - if (typeToCheck.IsNumericType()) + if (typeToCheck.IsNumericType(includeNullables: true)) { return true; } diff --git a/tests/AppMotor.Core.Tests/Tests/Extensions/TypeExtensionsTests.cs b/tests/AppMotor.Core.Tests/Tests/Extensions/TypeExtensionsTests.cs index 960a1772..a45fbc57 100644 --- a/tests/AppMotor.Core.Tests/Tests/Extensions/TypeExtensionsTests.cs +++ b/tests/AppMotor.Core.Tests/Tests/Extensions/TypeExtensionsTests.cs @@ -37,7 +37,14 @@ public sealed class TypeExtensionsTests [InlineData(typeof(decimal), true)] public void Test_IsNumericType(Type type, bool expectedResult) { - type.IsNumericType().ShouldBe(expectedResult); + type.IsNumericType(includeNullables: false).ShouldBe(expectedResult); + + if (expectedResult) + { + var nullableType = typeof(Nullable<>).MakeGenericType(type); + nullableType.IsNumericType(includeNullables: false).ShouldBe(false); + nullableType.IsNumericType(includeNullables: true).ShouldBe(true); + } } [Theory] @@ -63,7 +70,14 @@ public void Test_IsNumericType(Type type, bool expectedResult) [InlineData(typeof(decimal), false)] public void Test_IsNumericIntegerType(Type type, bool expectedResult) { - type.IsNumericIntegerType().ShouldBe(expectedResult); + type.IsNumericIntegerType(includeNullables: false).ShouldBe(expectedResult); + + if (expectedResult) + { + var nullableType = typeof(Nullable<>).MakeGenericType(type); + nullableType.IsNumericIntegerType(includeNullables: false).ShouldBe(false); + nullableType.IsNumericIntegerType(includeNullables: true).ShouldBe(true); + } } [Theory] @@ -89,7 +103,14 @@ public void Test_IsNumericIntegerType(Type type, bool expectedResult) [InlineData(typeof(decimal), true)] public void Test_IsNumericFloatType(Type type, bool expectedResult) { - type.IsNumericFloatType().ShouldBe(expectedResult); + type.IsNumericFloatType(includeNullables: false).ShouldBe(expectedResult); + + if (expectedResult) + { + var nullableType = typeof(Nullable<>).MakeGenericType(type); + nullableType.IsNumericFloatType(includeNullables: false).ShouldBe(false); + nullableType.IsNumericFloatType(includeNullables: true).ShouldBe(true); + } } [Fact]