diff --git a/src/projects/EnsureThat/Enforcers/TypeArg.cs b/src/projects/EnsureThat/Enforcers/TypeArg.cs index a549b3d..483d971 100644 --- a/src/projects/EnsureThat/Enforcers/TypeArg.cs +++ b/src/projects/EnsureThat/Enforcers/TypeArg.cs @@ -160,6 +160,70 @@ public Type IsNotOfType([ValidatedNotNull]Type param, Type nonExpectedType, [Inv return param; } + [NotNull] + [ContractAnnotation("param:null => halt")] + public T IsAssignableToType([ValidatedNotNull]T param, Type expectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) where T : class + { + Ensure.Any.IsNotNull(param, paramName, optsFn); + + IsAssignableToType(param.GetType(), expectedType, paramName, optsFn); + + return param; + } + + [NotNull] + [ContractAnnotation("param:null => halt")] + public Type IsAssignableToType([ValidatedNotNull]Type param, Type expectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) + { + Ensure.Any.IsNotNull(param, paramName, optsFn); + Ensure.Any.IsNotNull(expectedType, nameof(expectedType)); + +#if NETSTANDARD1_1 + // According to: https://devblogs.microsoft.com/dotnet/porting-to-net-core/. + if (!expectedType.GetTypeInfo().IsAssignableFrom(param.GetTypeInfo())) +#else + if (!expectedType.IsAssignableFrom(param)) +#endif + throw Ensure.ExceptionFactory.ArgumentException( + string.Format(ExceptionMessages.Types_IsAssignableToType_Failed, expectedType.FullName, param.FullName), + paramName, + optsFn); + + return param; + } + + [NotNull] + [ContractAnnotation("param:null => halt")] + public T IsNotAssignableToType([ValidatedNotNull]T param, Type nonExpectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) where T : class + { + Ensure.Any.IsNotNull(param, paramName, optsFn); + + IsNotAssignableToType(param.GetType(), nonExpectedType, paramName, optsFn); + + return param; + } + + [NotNull] + [ContractAnnotation("param:null => halt")] + public Type IsNotAssignableToType([ValidatedNotNull]Type param, Type nonExpectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) + { + Ensure.Any.IsNotNull(param, paramName, optsFn); + Ensure.Any.IsNotNull(nonExpectedType, nameof(nonExpectedType)); + +#if NETSTANDARD1_1 + // According to: https://devblogs.microsoft.com/dotnet/porting-to-net-core/. + if (nonExpectedType.GetTypeInfo().IsAssignableFrom(param.GetTypeInfo())) +#else + if (nonExpectedType.IsAssignableFrom(param)) +#endif + throw Ensure.ExceptionFactory.ArgumentException( + string.Format(ExceptionMessages.Types_IsNotAssignableToType_Failed, nonExpectedType.FullName), + paramName, + optsFn); + + return param; + } + [NotNull] [ContractAnnotation("param:null => halt")] public T IsClass([ValidatedNotNull]T param, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) diff --git a/src/projects/EnsureThat/EnsureArg.Types.cs b/src/projects/EnsureThat/EnsureArg.Types.cs index 443506f..2fb1fa5 100644 --- a/src/projects/EnsureThat/EnsureArg.Types.cs +++ b/src/projects/EnsureThat/EnsureArg.Types.cs @@ -106,6 +106,26 @@ public static object IsNotOfType([ValidatedNotNull]object param, Type nonExpecte public static Type IsNotOfType([ValidatedNotNull]Type param, Type nonExpectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) => Ensure.Type.IsNotOfType(param, nonExpectedType, paramName, optsFn); + [NotNull] + [ContractAnnotation("param:null => halt")] + public static object IsAssignableToType([ValidatedNotNull] object param, Type expectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) + => Ensure.Type.IsAssignableToType(param, expectedType, paramName, optsFn); + + [NotNull] + [ContractAnnotation("param:null => halt")] + public static Type IsAssignableToType([ValidatedNotNull]Type param, Type expectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) + => Ensure.Type.IsAssignableToType(param, expectedType, paramName, optsFn); + + [NotNull] + [ContractAnnotation("param:null => halt")] + public static object IsNotAssignableToType([ValidatedNotNull]object param, Type nonExpectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) + => Ensure.Type.IsNotAssignableToType(param, nonExpectedType, paramName, optsFn); + + [NotNull] + [ContractAnnotation("param:null => halt")] + public static Type IsNotAssignableToType([ValidatedNotNull]Type param, Type nonExpectedType, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) + => Ensure.Type.IsNotAssignableToType(param, nonExpectedType, paramName, optsFn); + [NotNull] [ContractAnnotation("param:null => halt")] public static object IsClass([ValidatedNotNull]object param, [InvokerParameterName] string paramName = null, OptsFn optsFn = null) diff --git a/src/projects/EnsureThat/EnsureThatTypeExtensions.cs b/src/projects/EnsureThat/EnsureThatTypeExtensions.cs index 4d4165d..7b10b88 100644 --- a/src/projects/EnsureThat/EnsureThatTypeExtensions.cs +++ b/src/projects/EnsureThat/EnsureThatTypeExtensions.cs @@ -35,6 +35,12 @@ public static void IsOfType(this in TypeParam param, [NotNull] Type expectedType public static void IsNotOfType(this in TypeParam param, Type expectedType) => Ensure.Type.IsNotOfType(param.Type, expectedType, param.Name, param.OptsFn); + public static void IsAssignableToType(this in TypeParam param, [NotNull] Type expectedType) + => Ensure.Type.IsAssignableToType(param.Type, expectedType, param.Name, param.OptsFn); + + public static void IsNotAssignableToType(this in TypeParam param, Type expectedType) + => Ensure.Type.IsNotAssignableToType(param.Type, expectedType, param.Name, param.OptsFn); + public static void IsClass(this in Param param) => Ensure.Type.IsClass(param.Value, param.Name, param.OptsFn); diff --git a/src/projects/EnsureThat/ExceptionMessages.cs b/src/projects/EnsureThat/ExceptionMessages.cs index c47cf19..254034b 100644 --- a/src/projects/EnsureThat/ExceptionMessages.cs +++ b/src/projects/EnsureThat/ExceptionMessages.cs @@ -40,6 +40,8 @@ public static class ExceptionMessages public static string Types_IsOfType_Failed { get; } = "The param is not of expected type. Expected: '{0}'. Got: '{1}'."; public static string Types_IsNotOfType_Failed { get; } = "The param was expected to not be of the type: '{0}'. But it was."; + public static string Types_IsAssignableToType_Failed { get; } = "The param is not assignable to the expected type. Expected: '{0}'. Got: '{1}'."; + public static string Types_IsNotAssignableToType_Failed { get; } = "The param was expected to not be assignable to the type: '{0}'. But it was."; public static string Types_IsClass_Failed_Null { get; } = "The param was expected to be a class, but was NULL."; public static string Types_IsClass_Failed { get; } = "The param was expected to be a class, but was type of: '{0}'."; diff --git a/src/tests/UnitTests/EnsureTypeParamTests.cs b/src/tests/UnitTests/EnsureTypeParamTests.cs index c576467..bc27a35 100644 --- a/src/tests/UnitTests/EnsureTypeParamTests.cs +++ b/src/tests/UnitTests/EnsureTypeParamTests.cs @@ -10,10 +10,14 @@ private class Bogus { } private class NonBogus { } + private class AssignableToNonBogus : NonBogus { } + private static readonly Type BogusType = typeof(Bogus); private static readonly Type NonBogusType = typeof(NonBogus); + private static readonly Type AssignableToNonBogusType = typeof(AssignableToNonBogus); + [Fact] public void IsOfType_WhenNotTypeOf_ThrowsArgumentException() => AssertIsOfTypeScenario( NonBogusType, BogusType, @@ -52,6 +56,56 @@ public void IsNotOfType_WhenIsNotTheType_It_should_not_throw() => ShouldNotThrow () => Ensure.ThatType(typeof(Bogus), ParamName).IsNotOfType(NonBogusType), () => Ensure.ThatTypeFor(new Bogus(), ParamName).IsNotOfType(NonBogusType)); + [Fact] + public void IsAssignableToType_WhenNotAssignableToType_ThrowsArgumentException() => AssertIsAssignableToTypeScenario( + NonBogusType, BogusType, + () => Ensure.Type.IsAssignableToType(typeof(Bogus), NonBogusType, ParamName), + () => Ensure.Type.IsAssignableToType(new Bogus(), NonBogusType, ParamName), + () => EnsureArg.IsAssignableToType(typeof(Bogus), NonBogusType, ParamName), + () => EnsureArg.IsAssignableToType(new Bogus(), NonBogusType, ParamName), + () => Ensure.ThatType(typeof(Bogus), ParamName).IsAssignableToType(NonBogusType), + () => Ensure.ThatTypeFor(new Bogus(), ParamName).IsAssignableToType(NonBogusType)); + + [Fact] + public void IsAssignableToType_WhenAssignableToType_It_should_not_throw() => ShouldNotThrow( + () => Ensure.Type.IsAssignableToType(NonBogusType, NonBogusType, ParamName), + () => Ensure.Type.IsAssignableToType(new NonBogus(), NonBogusType, ParamName), + () => EnsureArg.IsAssignableToType(NonBogusType, NonBogusType, ParamName), + () => EnsureArg.IsAssignableToType(new NonBogus(), NonBogusType, ParamName), + () => Ensure.ThatType(typeof(NonBogus), ParamName).IsAssignableToType(NonBogusType), + () => Ensure.ThatTypeFor(new NonBogus(), ParamName).IsAssignableToType(NonBogusType), + () => Ensure.Type.IsAssignableToType(AssignableToNonBogusType, NonBogusType, ParamName), + () => Ensure.Type.IsAssignableToType(new AssignableToNonBogus(), NonBogusType, ParamName), + () => EnsureArg.IsAssignableToType(AssignableToNonBogusType, NonBogusType, ParamName), + () => EnsureArg.IsAssignableToType(new AssignableToNonBogus(), NonBogusType, ParamName), + () => Ensure.ThatType(typeof(AssignableToNonBogus), ParamName).IsAssignableToType(NonBogusType), + () => Ensure.ThatTypeFor(new AssignableToNonBogus(), ParamName).IsAssignableToType(NonBogusType)); + + [Fact] + public void IsNotAssignableToType_WhenAssignableToType_ThrowsArgumentException() => ShouldThrow( + string.Format(ExceptionMessages.Types_IsNotAssignableToType_Failed, NonBogusType), + () => Ensure.Type.IsNotAssignableToType(typeof(NonBogus), NonBogusType, ParamName), + () => Ensure.Type.IsNotAssignableToType(new NonBogus(), NonBogusType, ParamName), + () => EnsureArg.IsNotAssignableToType(typeof(NonBogus), NonBogusType, ParamName), + () => EnsureArg.IsNotAssignableToType(new NonBogus(), NonBogusType, ParamName), + () => Ensure.ThatType(typeof(NonBogus), ParamName).IsNotAssignableToType(NonBogusType), + () => Ensure.ThatTypeFor(new NonBogus(), ParamName).IsNotAssignableToType(NonBogusType), + () => Ensure.Type.IsNotAssignableToType(typeof(AssignableToNonBogus), NonBogusType, ParamName), + () => Ensure.Type.IsNotAssignableToType(new AssignableToNonBogus(), NonBogusType, ParamName), + () => EnsureArg.IsNotAssignableToType(typeof(AssignableToNonBogus), NonBogusType, ParamName), + () => EnsureArg.IsNotAssignableToType(new AssignableToNonBogus(), NonBogusType, ParamName), + () => Ensure.ThatType(typeof(AssignableToNonBogus), ParamName).IsNotAssignableToType(NonBogusType), + () => Ensure.ThatTypeFor(new AssignableToNonBogus(), ParamName).IsNotAssignableToType(NonBogusType)); + + [Fact] + public void IsNotAssignableToType_WhenNotAssignableToType_It_should_not_throw() => ShouldNotThrow( + () => Ensure.Type.IsNotAssignableToType(BogusType, NonBogusType, ParamName), + () => Ensure.Type.IsNotAssignableToType(new Bogus(), NonBogusType, ParamName), + () => EnsureArg.IsNotAssignableToType(BogusType, NonBogusType, ParamName), + () => EnsureArg.IsNotAssignableToType(new Bogus(), NonBogusType, ParamName), + () => Ensure.ThatType(typeof(Bogus), ParamName).IsNotAssignableToType(NonBogusType), + () => Ensure.ThatTypeFor(new Bogus(), ParamName).IsNotAssignableToType(NonBogusType)); + [Fact] public void IsInt_WhenNotTypeOf_ThrowsArgumentException() => AssertIsOfTypeScenario( typeof(int), typeof(decimal), @@ -258,6 +312,9 @@ public void IsClass_WhenIsClass_It_should_not_throw() private static void AssertIsOfTypeScenario(Type expected, Type actual, params Action[] actions) => ShouldThrow(string.Format(ExceptionMessages.Types_IsOfType_Failed, expected.FullName, actual.FullName), actions); + private static void AssertIsAssignableToTypeScenario(Type expected, Type actual, params Action[] actions) + => ShouldThrow(string.Format(ExceptionMessages.Types_IsAssignableToType_Failed, expected.FullName, actual.FullName), actions); + private static void AssertIsNotClass(Type type, params Action[] actions) => ShouldThrow(string.Format(ExceptionMessages.Types_IsClass_Failed, type.FullName), actions);