diff --git a/ICD.Common.Utils.Tests/Extensions/TypeExtensionsTest.cs b/ICD.Common.Utils.Tests/Extensions/TypeExtensionsTest.cs index 1f8fb59..f6450bb 100644 --- a/ICD.Common.Utils.Tests/Extensions/TypeExtensionsTest.cs +++ b/ICD.Common.Utils.Tests/Extensions/TypeExtensionsTest.cs @@ -10,6 +10,14 @@ namespace ICD.Common.Utils.Tests.Extensions [TestFixture] public sealed class TypeExtensionsTest { + [TestCase(typeof(byte), false)] + [TestCase(typeof(byte?), true)] + [TestCase(typeof(string), true)] + public void CanBeNullTest(Type value, bool expected) + { + Assert.AreEqual(expected, value.CanBeNull()); + } + [TestCase(typeof(byte), true)] [TestCase(typeof(decimal), true)] [TestCase(typeof(double), true)] @@ -61,6 +69,23 @@ namespace ICD.Common.Utils.Tests.Extensions Assert.AreEqual(expected, value.IsDecimalNumeric()); } + [TestCase(typeof(byte), true)] + [TestCase(typeof(decimal), false)] + [TestCase(typeof(double), false)] + [TestCase(typeof(float), false)] + [TestCase(typeof(int), true)] + [TestCase(typeof(long), true)] + [TestCase(typeof(sbyte), true)] + [TestCase(typeof(short), true)] + [TestCase(typeof(uint), true)] + [TestCase(typeof(ulong), true)] + [TestCase(typeof(ushort), true)] + [TestCase(typeof(string), false)] + public void IsIntegerNumericTest(Type value, bool expected) + { + Assert.AreEqual(expected, value.IsIntegerNumeric()); + } + [TestCase(typeof(string), typeof(object), true)] [TestCase(typeof(object), typeof(string), false)] public void IsAssignableToTest(Type a, Type b, bool expected) diff --git a/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs b/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs index 81b34d9..21adc45 100644 --- a/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs +++ b/ICD.Common.Utils.Tests/ReflectionUtilsTest.cs @@ -121,9 +121,33 @@ namespace ICD.Common.Utils.Tests } [Test] - public void LoadAssemblyFromPath() + public void LoadAssemblyFromPathTest() { Assert.Inconclusive(); } + + [Test] + public void GetImplementationTest() + { + Assert.Inconclusive(); + } + + [Test] + public void ChangeTypeTest() + { + // Same type + Assert.AreEqual(10, ReflectionUtils.ChangeType(10, typeof(int))); + + // Null + Assert.AreEqual(null, ReflectionUtils.ChangeType(null, typeof(string))); + + // Enums + Assert.AreEqual(BindingFlags.GetProperty, ReflectionUtils.ChangeType((int)(object)BindingFlags.GetProperty, typeof(BindingFlags))); + Assert.AreEqual(BindingFlags.GetProperty, ReflectionUtils.ChangeType(BindingFlags.GetProperty.ToString(), typeof(BindingFlags))); + Assert.AreEqual(BindingFlags.GetProperty, ReflectionUtils.ChangeType(((int)(object)BindingFlags.GetProperty).ToString(), typeof(BindingFlags))); + + // Everything else + Assert.AreEqual(10, ReflectionUtils.ChangeType("10", typeof(int))); + } } } diff --git a/ICD.Common.Utils/Extensions/TypeExtensions.cs b/ICD.Common.Utils/Extensions/TypeExtensions.cs index ad0a70d..34829aa 100644 --- a/ICD.Common.Utils/Extensions/TypeExtensions.cs +++ b/ICD.Common.Utils/Extensions/TypeExtensions.cs @@ -45,6 +45,18 @@ namespace ICD.Common.Utils.Extensions typeof(float), }; + private static readonly IcdHashSet s_IntegerNumericTypes = new IcdHashSet + { + typeof(byte), + typeof(int), + typeof(long), + typeof(sbyte), + typeof(short), + typeof(uint), + typeof(ulong), + typeof(ushort) + }; + private static readonly Dictionary s_TypeAllTypes; private static readonly Dictionary s_TypeBaseTypes; private static readonly Dictionary s_TypeImmediateInterfaces; @@ -61,6 +73,19 @@ namespace ICD.Common.Utils.Extensions s_TypeMinimalInterfaces = new Dictionary(); } + /// + /// Returns true if the given type can represent a null value. + /// + /// + /// + public static bool CanBeNull(this Type extends) + { + if (extends == null) + throw new ArgumentException("extends"); + + return !extends.IsValueType || Nullable.GetUnderlyingType(extends) != null; + } + /// /// Returns true if the given type is a numeric type. /// @@ -100,6 +125,24 @@ namespace ICD.Common.Utils.Extensions return s_DecimalNumericTypes.Contains(extends); } + /// + /// Returns true if the given type is an integer numeric type. + /// + /// + /// + public static bool IsIntegerNumeric(this Type extends) + { + if (extends == null) + throw new ArgumentException("extends"); + + return s_IntegerNumericTypes.Contains(extends); + } + + /// + /// Gets the Assembly containing the type. + /// + /// + /// public static Assembly GetAssembly(this Type extends) { if (extends == null) @@ -114,6 +157,12 @@ namespace ICD.Common.Utils.Extensions .Assembly; } + /// + /// Returns true if the type is assignable to the given type. + /// + /// + /// + /// public static bool IsAssignableTo(this Type from, Type to) { if (from == null) diff --git a/ICD.Common.Utils/ReflectionUtils.cs b/ICD.Common.Utils/ReflectionUtils.cs index cdf400a..2a4733d 100644 --- a/ICD.Common.Utils/ReflectionUtils.cs +++ b/ICD.Common.Utils/ReflectionUtils.cs @@ -368,5 +368,42 @@ namespace ICD.Common.Utils ? property : GetImplementation(property.DeclaringType, property); } + + /// + /// Changes the given value to the given type. + /// + /// + /// + /// + public static object ChangeType(object value, Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + // Handle null value + if (value == null) + { + if (type.CanBeNull()) + return null; + + throw new InvalidCastException(); + } + + Type valueType = value.GetType(); + if (valueType.IsAssignableTo(type)) + return value; + + // Handle enum + if (type.IsEnum) + { + if (valueType.IsIntegerNumeric()) + return Enum.ToObject(type, value); + + if (value is string) + return Enum.Parse(type, value as string, false); + } + + return Convert.ChangeType(value, type, null); + } } }