From a96973e5c3dd30b520b812069496032513838147 Mon Sep 17 00:00:00 2001 From: Chris Donnelly Date: Wed, 30 Jul 2025 12:47:02 -0500 Subject: [PATCH 01/10] fix: ParseSpanDelegatesFile test The test was broken on commit 34efb3d0539c867fd0735d0dcca3b712d360652a, which moved the file to the ExceptionFactory subdirectory but did not update the test. Test passes again. --- .../ParseCSharpFilesWithRoslynTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Light.GuardClauses.SourceCodeTransformation.Tests/ParseCSharpFilesWithRoslynTests.cs b/Code/Light.GuardClauses.SourceCodeTransformation.Tests/ParseCSharpFilesWithRoslynTests.cs index cb38711..00f0f8f 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation.Tests/ParseCSharpFilesWithRoslynTests.cs +++ b/Code/Light.GuardClauses.SourceCodeTransformation.Tests/ParseCSharpFilesWithRoslynTests.cs @@ -29,7 +29,7 @@ public static void ParseExpressionExtensionsFile() [Fact] public static void ParseSpanDelegatesFile() { - var fileInfo = GetLightGuardClausesFile("SpanDelegates.cs"); + var fileInfo = GetLightGuardClausesFile(@"ExceptionFactory\SpanDelegates.cs"); var syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(fileInfo.FullName), new CSharpParseOptions(LanguageVersion.CSharp7_3, preprocessorSymbols: new[] { "NETSTANDARD2_0" })); var root = (CompilationUnitSyntax) syntaxTree.GetRoot(); From c7a1359845eb4f44ce7639fe2111437f76836da3 Mon Sep 17 00:00:00 2001 From: Chris Donnelly Date: Wed, 30 Jul 2025 12:35:07 -0500 Subject: [PATCH 02/10] fix: use NET8_0_OR_GREATER for ifdefs This allows one to use the SingleFile when targeting .NET 9 or 10, and still getting the .NET 8.0+ enhancements. Disclaimer: I have done this globally without evaluating every potential case, but it seems to work. --- .../CommonAssertions/IsApproximatelyTests.cs | 2 +- .../CommonAssertions/IsGreaterThanOrApproximatelyTests.cs | 2 +- .../CommonAssertions/IsLessThanOrApproximatelyTests.cs | 2 +- .../ComparableAssertions/MustBeApproximatelyTests.cs | 4 ++-- .../MustBeGreaterThanOrApproximatelyTests.cs | 4 ++-- .../MustBeLessThanOrApproximatelyTests.cs | 4 ++-- .../ComparableAssertions/MustNotBeApproximatelyTests.cs | 4 ++-- .../StringAssertions/IsEmailAddressTests.cs | 2 +- .../StringAssertions/MustBeEmailAddressTests.cs | 4 ++-- .../Light.GuardClauses/CallerArgumentExpressionAttribute.cs | 2 +- Code/Light.GuardClauses/Check.DerivesFrom.cs | 2 +- Code/Light.GuardClauses/Check.Implements.cs | 6 +++--- Code/Light.GuardClauses/Check.InheritsFrom.cs | 6 +++--- Code/Light.GuardClauses/Check.IsApproximately.cs | 6 +++--- Code/Light.GuardClauses/Check.IsEmailAddress.cs | 4 ++-- .../Check.IsGreaterThanOrApproximately.cs | 6 +++--- Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs | 6 +++--- .../Check.IsOpenConstructedGenericType.cs | 2 +- Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs | 2 +- Code/Light.GuardClauses/Check.IsOrImplements.cs | 6 +++--- Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs | 6 +++--- Code/Light.GuardClauses/Check.MustBeApproximately.cs | 6 +++--- Code/Light.GuardClauses/Check.MustBeEmailAddress.cs | 4 ++-- .../Check.MustBeGreaterThanOrApproximately.cs | 6 +++--- .../Check.MustBeLessThanOrApproximately.cs | 6 +++--- Code/Light.GuardClauses/Check.MustNotBeApproximately.cs | 6 +++--- Code/Light.GuardClauses/EnumInfo.cs | 2 +- Code/Light.GuardClauses/Exceptions/AbsoluteUriException.cs | 2 +- .../Exceptions/ArgumentDefaultException.cs | 2 +- Code/Light.GuardClauses/Exceptions/CollectionException.cs | 2 +- .../Exceptions/EmptyCollectionException.cs | 2 +- Code/Light.GuardClauses/Exceptions/EmptyGuidException.cs | 2 +- Code/Light.GuardClauses/Exceptions/EmptyStringException.cs | 2 +- .../Exceptions/EnumValueNotDefinedException.cs | 2 +- Code/Light.GuardClauses/Exceptions/ExistingItemException.cs | 2 +- .../Exceptions/InvalidCollectionCountException.cs | 2 +- .../Exceptions/InvalidConfigurationException.cs | 2 +- .../Exceptions/InvalidDateTimeException.cs | 2 +- .../Exceptions/InvalidEmailAddressException.cs | 4 ++-- Code/Light.GuardClauses/Exceptions/InvalidStateException.cs | 2 +- .../Exceptions/InvalidUriSchemeException.cs | 2 +- Code/Light.GuardClauses/Exceptions/MissingItemException.cs | 2 +- .../Exceptions/NullableHasNoValueException.cs | 2 +- Code/Light.GuardClauses/Exceptions/RelativeUriException.cs | 2 +- .../Exceptions/SameObjectReferenceException.cs | 2 +- .../Exceptions/StringDoesNotMatchException.cs | 2 +- Code/Light.GuardClauses/Exceptions/StringException.cs | 2 +- Code/Light.GuardClauses/Exceptions/StringLengthException.cs | 2 +- Code/Light.GuardClauses/Exceptions/SubstringException.cs | 2 +- Code/Light.GuardClauses/Exceptions/TypeCastException.cs | 2 +- Code/Light.GuardClauses/Exceptions/UriException.cs | 2 +- .../Exceptions/ValueIsNotOneOfException.cs | 2 +- Code/Light.GuardClauses/Exceptions/ValueIsOneOfException.cs | 2 +- Code/Light.GuardClauses/Exceptions/ValuesEqualException.cs | 2 +- .../Exceptions/ValuesNotEqualException.cs | 2 +- .../Exceptions/WhiteSpaceStringException.cs | 2 +- Code/Light.GuardClauses/RegularExpressions.cs | 4 ++-- 57 files changed, 88 insertions(+), 88 deletions(-) diff --git a/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs index 70db5a8..8803b9c 100644 --- a/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/CommonAssertions/IsApproximatelyTests.cs @@ -41,7 +41,7 @@ public static void FloatWithDefaultTolerance(float first, float second, bool exp public static void FloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => first.IsApproximately(second, tolerance).Should().Be(expected); -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [InlineData(1.1, 1.3, 0.5, true)] [InlineData(100.55, 100.555, 0.00001, false)] diff --git a/Code/Light.GuardClauses.Tests/CommonAssertions/IsGreaterThanOrApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/CommonAssertions/IsGreaterThanOrApproximatelyTests.cs index 96ccedc..f855b87 100644 --- a/Code/Light.GuardClauses.Tests/CommonAssertions/IsGreaterThanOrApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/CommonAssertions/IsGreaterThanOrApproximatelyTests.cs @@ -37,7 +37,7 @@ public static void FloatWithDefaultTolerance(float first, float second, bool exp public static void FloatWIthCustomTolerance(float first, float second, float tolerance, bool expected) => first.IsGreaterThanOrApproximately(second, tolerance).Should().Be(expected); -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [InlineData(15.91, 15.9, 0.1, true)] [InlineData(24.449, 24.45, 0.0001, false)] diff --git a/Code/Light.GuardClauses.Tests/CommonAssertions/IsLessThanOrApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/CommonAssertions/IsLessThanOrApproximatelyTests.cs index 6e774b8..5822182 100644 --- a/Code/Light.GuardClauses.Tests/CommonAssertions/IsLessThanOrApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/CommonAssertions/IsLessThanOrApproximatelyTests.cs @@ -37,7 +37,7 @@ public static void FloatWithDefaultTolerance(float first, float second, bool exp public static void FloatWithCustomTolerance(float first, float second, float tolerance, bool expected) => first.IsLessThanOrApproximately(second, tolerance).Should().Be(expected); -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [InlineData(13.25, 13.5, 0.1, true)] // Less than case [InlineData(13.5, 13.5, 0.1, true)] // Equal case diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeApproximatelyTests.cs index 3204655..20e87f2 100644 --- a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeApproximatelyTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using FluentAssertions; using Xunit; @@ -178,7 +178,7 @@ public static void CallerArgumentExpressionWithTolerance_Float() .WithParameterName(nameof(pi)); } -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [InlineData(5.1, 5.0, 0.2)] [InlineData(10.3, 10.3, 0.01)] diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeGreaterThanOrApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeGreaterThanOrApproximatelyTests.cs index ff19f60..5a76566 100644 --- a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeGreaterThanOrApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeGreaterThanOrApproximatelyTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using FluentAssertions; using Xunit; @@ -202,7 +202,7 @@ public static void CallerArgumentExpressionWithTolerance_Float() .WithParameterName(nameof(pi)); } -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [InlineData(15.91, 15.9, 0.1)] [InlineData(24.4999, 24.45, 0.0001)] diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeLessThanOrApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeLessThanOrApproximatelyTests.cs index 9eace40..25c7f1b 100644 --- a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeLessThanOrApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustBeLessThanOrApproximatelyTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using FluentAssertions; using Xunit; @@ -202,7 +202,7 @@ public static void CallerArgumentExpressionWithTolerance_Float() .WithParameterName(nameof(threePointFive)); } -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [InlineData(15.9, 15.91, 0.1)] [InlineData(24.45, 24.4999, 0.0001)] diff --git a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeApproximatelyTests.cs b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeApproximatelyTests.cs index da9e7ea..5276492 100644 --- a/Code/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeApproximatelyTests.cs +++ b/Code/Light.GuardClauses.Tests/ComparableAssertions/MustNotBeApproximatelyTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using FluentAssertions; using Xunit; @@ -180,7 +180,7 @@ public static void CallerArgumentExpressionWithTolerance_Float() .WithParameterName(nameof(pi)); } -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [InlineData(5.3, 5.0, 0.2)] [InlineData(10.4, 10.3, 0.01)] diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs index 0cd3750..c252cbf 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/IsEmailAddressTests.cs @@ -24,7 +24,7 @@ public void IsValidEmailAddress(string email) isValid.Should().BeTrue(); } -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [ClassData(typeof(InvalidEmailAddressesWithNull))] public void IsNotValidEmailAddress_ReadOnlySpan(string email) diff --git a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeEmailAddressTests.cs b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeEmailAddressTests.cs index f6d1314..81c4c61 100644 --- a/Code/Light.GuardClauses.Tests/StringAssertions/MustBeEmailAddressTests.cs +++ b/Code/Light.GuardClauses.Tests/StringAssertions/MustBeEmailAddressTests.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Text.RegularExpressions; using FluentAssertions; using Light.GuardClauses.Exceptions; @@ -124,7 +124,7 @@ public static void CallerArgumentExpression() .WithParameterName(nameof(email)); } -#if NET8_0 +#if NET8_0_OR_GREATER [Theory] [ClassData(typeof(ValidEmailAddresses))] public static void ValidEmailAddress_ReadOnlySpan(string email) diff --git a/Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs b/Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs index f5df610..274950d 100644 --- a/Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs +++ b/Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs @@ -1,4 +1,4 @@ -#if !NET8_0 +#if !NET8_0_OR_GREATER // ReSharper disable once CheckNamespace -- CallerArgumentExpression must be in exactly this namespace namespace System.Runtime.CompilerServices; diff --git a/Code/Light.GuardClauses/Check.DerivesFrom.cs b/Code/Light.GuardClauses/Check.DerivesFrom.cs index 8be3fd3..bd07dc3 100644 --- a/Code/Light.GuardClauses/Check.DerivesFrom.cs +++ b/Code/Light.GuardClauses/Check.DerivesFrom.cs @@ -1,4 +1,4 @@ -#if NET8_0 +#if NET8_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System; diff --git a/Code/Light.GuardClauses/Check.Implements.cs b/Code/Light.GuardClauses/Check.Implements.cs index e0643d4..55262ac 100644 --- a/Code/Light.GuardClauses/Check.Implements.cs +++ b/Code/Light.GuardClauses/Check.Implements.cs @@ -1,4 +1,4 @@ -#if NET8_0 +#if NET8_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System; @@ -21,7 +21,7 @@ public static partial class Check /// Thrown when or is null. [ContractAnnotation("type:null => halt; interfaceType:null => halt")] public static bool Implements( -#if NET8_0 +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif [NotNull] [ValidatedNotNull] this Type type, @@ -53,7 +53,7 @@ [NotNull] [ValidatedNotNull] Type interfaceType /// Thrown when , or , or is null. [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] public static bool Implements( -#if NET8_0 +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif [NotNull] [ValidatedNotNull] this Type type, diff --git a/Code/Light.GuardClauses/Check.InheritsFrom.cs b/Code/Light.GuardClauses/Check.InheritsFrom.cs index 6e2ad44..30403f5 100644 --- a/Code/Light.GuardClauses/Check.InheritsFrom.cs +++ b/Code/Light.GuardClauses/Check.InheritsFrom.cs @@ -1,4 +1,4 @@ -#if NET8_0 +#if NET8_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System; @@ -23,7 +23,7 @@ public static partial class Check [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] public static bool InheritsFrom( -#if NET8_0 +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif [NotNull] [ValidatedNotNull] this Type type, @@ -46,7 +46,7 @@ [NotNull] [ValidatedNotNull] Type baseClassOrInterfaceType [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] public static bool InheritsFrom( -#if NET8_0 +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif [NotNull] [ValidatedNotNull] this Type type, diff --git a/Code/Light.GuardClauses/Check.IsApproximately.cs b/Code/Light.GuardClauses/Check.IsApproximately.cs index 02ef101..5d6308f 100644 --- a/Code/Light.GuardClauses/Check.IsApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsApproximately.cs @@ -1,6 +1,6 @@ -using System; +using System; using System.Runtime.CompilerServices; -#if NET8_0 +#if NET8_0_OR_GREATER using System.Numerics; #endif @@ -62,7 +62,7 @@ public static bool IsApproximately(this float value, float other, float toleranc public static bool IsApproximately(this float value, float other) => Math.Abs(value - other) <= 0.0001f; -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Checks if the specified value is approximately the same as the other value, using the given tolerance. /// diff --git a/Code/Light.GuardClauses/Check.IsEmailAddress.cs b/Code/Light.GuardClauses/Check.IsEmailAddress.cs index 396e2fd..d795937 100644 --- a/Code/Light.GuardClauses/Check.IsEmailAddress.cs +++ b/Code/Light.GuardClauses/Check.IsEmailAddress.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; @@ -29,7 +29,7 @@ public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) => emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Checks if the specified span is an email address using the default email regular expression /// defined in . diff --git a/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs b/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs index 487540b..a04ec86 100644 --- a/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsGreaterThanOrApproximately.cs @@ -1,5 +1,5 @@ -using System.Runtime.CompilerServices; -#if NET8_0 +using System.Runtime.CompilerServices; +#if NET8_0_OR_GREATER using System.Numerics; #endif @@ -61,7 +61,7 @@ public static bool IsGreaterThanOrApproximately(this float value, float other, f public static bool IsGreaterThanOrApproximately(this float value, float other) => value > other || value.IsApproximately(other); -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. /// diff --git a/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs b/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs index 89b109b..50ed29b 100644 --- a/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs +++ b/Code/Light.GuardClauses/Check.IsLessThanOrApproximately.cs @@ -1,5 +1,5 @@ -using System.Runtime.CompilerServices; -#if NET8_0 +using System.Runtime.CompilerServices; +#if NET8_0_OR_GREATER using System.Numerics; #endif @@ -61,7 +61,7 @@ public static bool IsLessThanOrApproximately(this float value, float other, floa public static bool IsLessThanOrApproximately(this float value, float other) => value < other || value.IsApproximately(other); -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. /// diff --git a/Code/Light.GuardClauses/Check.IsOpenConstructedGenericType.cs b/Code/Light.GuardClauses/Check.IsOpenConstructedGenericType.cs index 7d8348e..bb7d462 100644 --- a/Code/Light.GuardClauses/Check.IsOpenConstructedGenericType.cs +++ b/Code/Light.GuardClauses/Check.IsOpenConstructedGenericType.cs @@ -1,4 +1,4 @@ -#if NET8_0 +#if NET8_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System; diff --git a/Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs b/Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs index ed74bee..3152886 100644 --- a/Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs +++ b/Code/Light.GuardClauses/Check.IsOrDerivesFrom.cs @@ -1,4 +1,4 @@ -#if NET8_0 +#if NET8_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System; diff --git a/Code/Light.GuardClauses/Check.IsOrImplements.cs b/Code/Light.GuardClauses/Check.IsOrImplements.cs index aea0e10..ec096a2 100644 --- a/Code/Light.GuardClauses/Check.IsOrImplements.cs +++ b/Code/Light.GuardClauses/Check.IsOrImplements.cs @@ -1,4 +1,4 @@ -#if NET8_0 +#if NET8_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System; @@ -23,7 +23,7 @@ public static partial class Check [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt; otherType:null => halt")] public static bool IsOrImplements( -#if NET8_0 +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif [NotNull] [ValidatedNotNull] this Type type, @@ -43,7 +43,7 @@ [NotNull] [ValidatedNotNull] Type otherType [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt; otherType:null => halt")] public static bool IsOrImplements( -#if NET8_0 +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif [NotNull] [ValidatedNotNull] this Type type, diff --git a/Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs b/Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs index 62549f4..754ad60 100644 --- a/Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs +++ b/Code/Light.GuardClauses/Check.IsOrInheritsFrom.cs @@ -1,4 +1,4 @@ -#if NET8_0 +#if NET8_0_OR_GREATER using System.Diagnostics.CodeAnalysis; #endif using System; @@ -24,7 +24,7 @@ public static partial class Check [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt; otherType:null => halt")] public static bool IsOrInheritsFrom( -#if NET8_0 +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif [NotNull] [ValidatedNotNull] this Type type, @@ -44,7 +44,7 @@ [NotNull] [ValidatedNotNull] Type otherType [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] public static bool IsOrInheritsFrom( -#if NET8_0 +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] #endif [NotNull] [ValidatedNotNull] this Type type, diff --git a/Code/Light.GuardClauses/Check.MustBeApproximately.cs b/Code/Light.GuardClauses/Check.MustBeApproximately.cs index 77bdf05..effb46a 100644 --- a/Code/Light.GuardClauses/Check.MustBeApproximately.cs +++ b/Code/Light.GuardClauses/Check.MustBeApproximately.cs @@ -1,7 +1,7 @@ -using System; +using System; using System.Runtime.CompilerServices; using Light.GuardClauses.ExceptionFactory; -#if NET8_0 +#if NET8_0_OR_GREATER using System.Numerics; #endif @@ -232,7 +232,7 @@ Func exceptionFactory return parameter; } -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Ensures that the specified is approximately equal to the given /// value, or otherwise throws an . diff --git a/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs b/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs index 8cd52c7..cf7f1e5 100644 --- a/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs +++ b/Code/Light.GuardClauses/Check.MustBeEmailAddress.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using JetBrains.Annotations; @@ -109,7 +109,7 @@ public static string MustBeEmailAddress( return parameter; } -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Ensures that the span represents a valid email address using the default email regular expression /// defined in , or otherwise throws an . diff --git a/Code/Light.GuardClauses/Check.MustBeGreaterThanOrApproximately.cs b/Code/Light.GuardClauses/Check.MustBeGreaterThanOrApproximately.cs index a0cd395..0314912 100644 --- a/Code/Light.GuardClauses/Check.MustBeGreaterThanOrApproximately.cs +++ b/Code/Light.GuardClauses/Check.MustBeGreaterThanOrApproximately.cs @@ -1,7 +1,7 @@ -using System; +using System; using System.Runtime.CompilerServices; using Light.GuardClauses.ExceptionFactory; -#if NET8_0 +#if NET8_0_OR_GREATER using System.Numerics; #endif @@ -227,7 +227,7 @@ Func exceptionFactory return parameter; } -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Ensures that the specified is greater than or approximately equal to the given /// value, or otherwise throws an . diff --git a/Code/Light.GuardClauses/Check.MustBeLessThanOrApproximately.cs b/Code/Light.GuardClauses/Check.MustBeLessThanOrApproximately.cs index f48bf5d..2b6e3e6 100644 --- a/Code/Light.GuardClauses/Check.MustBeLessThanOrApproximately.cs +++ b/Code/Light.GuardClauses/Check.MustBeLessThanOrApproximately.cs @@ -1,7 +1,7 @@ -using System; +using System; using System.Runtime.CompilerServices; using Light.GuardClauses.ExceptionFactory; -#if NET8_0 +#if NET8_0_OR_GREATER using System.Numerics; #endif @@ -227,7 +227,7 @@ Func exceptionFactory return parameter; } -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Ensures that the specified is less than or approximately equal to the given /// value, or otherwise throws an . diff --git a/Code/Light.GuardClauses/Check.MustNotBeApproximately.cs b/Code/Light.GuardClauses/Check.MustNotBeApproximately.cs index 909ffe0..fa62386 100644 --- a/Code/Light.GuardClauses/Check.MustNotBeApproximately.cs +++ b/Code/Light.GuardClauses/Check.MustNotBeApproximately.cs @@ -1,7 +1,7 @@ -using System; +using System; using System.Runtime.CompilerServices; using Light.GuardClauses.ExceptionFactory; -#if NET8_0 +#if NET8_0_OR_GREATER using System.Numerics; #endif @@ -232,7 +232,7 @@ Func exceptionFactory return parameter; } -#if NET8_0 +#if NET8_0_OR_GREATER /// /// Ensures that the specified is not approximately equal to the given /// value, or otherwise throws an . diff --git a/Code/Light.GuardClauses/EnumInfo.cs b/Code/Light.GuardClauses/EnumInfo.cs index 49232e9..d19b156 100644 --- a/Code/Light.GuardClauses/EnumInfo.cs +++ b/Code/Light.GuardClauses/EnumInfo.cs @@ -35,7 +35,7 @@ public static class EnumInfo where T : struct, Enum static EnumInfo() { -#if NET8_0 +#if NET8_0_OR_GREATER EnumConstantsArray = Enum.GetValues(); #else EnumConstantsArray = (T[]) Enum.GetValues(typeof(T)); diff --git a/Code/Light.GuardClauses/Exceptions/AbsoluteUriException.cs b/Code/Light.GuardClauses/Exceptions/AbsoluteUriException.cs index 5d105ac..3e7ee49 100644 --- a/Code/Light.GuardClauses/Exceptions/AbsoluteUriException.cs +++ b/Code/Light.GuardClauses/Exceptions/AbsoluteUriException.cs @@ -16,7 +16,7 @@ public class AbsoluteUriException : UriException /// The message of the exception (optional). public AbsoluteUriException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected AbsoluteUriException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/ArgumentDefaultException.cs b/Code/Light.GuardClauses/Exceptions/ArgumentDefaultException.cs index 7ca8b3b..c96991f 100644 --- a/Code/Light.GuardClauses/Exceptions/ArgumentDefaultException.cs +++ b/Code/Light.GuardClauses/Exceptions/ArgumentDefaultException.cs @@ -16,7 +16,7 @@ public class ArgumentDefaultException : ArgumentException /// The message of the exception (optional). public ArgumentDefaultException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected ArgumentDefaultException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/CollectionException.cs b/Code/Light.GuardClauses/Exceptions/CollectionException.cs index f585e4c..5d1a8de 100644 --- a/Code/Light.GuardClauses/Exceptions/CollectionException.cs +++ b/Code/Light.GuardClauses/Exceptions/CollectionException.cs @@ -16,7 +16,7 @@ public class CollectionException : ArgumentException /// The message of the exception (optional). public CollectionException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected CollectionException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/EmptyCollectionException.cs b/Code/Light.GuardClauses/Exceptions/EmptyCollectionException.cs index c08640b..a499ebc 100644 --- a/Code/Light.GuardClauses/Exceptions/EmptyCollectionException.cs +++ b/Code/Light.GuardClauses/Exceptions/EmptyCollectionException.cs @@ -16,7 +16,7 @@ public class EmptyCollectionException : InvalidCollectionCountException /// The message of the exception (optional). public EmptyCollectionException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected EmptyCollectionException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/EmptyGuidException.cs b/Code/Light.GuardClauses/Exceptions/EmptyGuidException.cs index 64dd4c3..0ddb245 100644 --- a/Code/Light.GuardClauses/Exceptions/EmptyGuidException.cs +++ b/Code/Light.GuardClauses/Exceptions/EmptyGuidException.cs @@ -16,7 +16,7 @@ public class EmptyGuidException : ArgumentException /// The message of the exception (optional). public EmptyGuidException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected EmptyGuidException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/EmptyStringException.cs b/Code/Light.GuardClauses/Exceptions/EmptyStringException.cs index 4b37349..a7a41f7 100644 --- a/Code/Light.GuardClauses/Exceptions/EmptyStringException.cs +++ b/Code/Light.GuardClauses/Exceptions/EmptyStringException.cs @@ -16,7 +16,7 @@ public class EmptyStringException : StringException /// The message of the exception (optional). public EmptyStringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/EnumValueNotDefinedException.cs b/Code/Light.GuardClauses/Exceptions/EnumValueNotDefinedException.cs index ec2c375..8f536db 100644 --- a/Code/Light.GuardClauses/Exceptions/EnumValueNotDefinedException.cs +++ b/Code/Light.GuardClauses/Exceptions/EnumValueNotDefinedException.cs @@ -16,7 +16,7 @@ public class EnumValueNotDefinedException : ArgumentException /// The message of the exception. public EnumValueNotDefinedException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected EnumValueNotDefinedException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/ExistingItemException.cs b/Code/Light.GuardClauses/Exceptions/ExistingItemException.cs index 52f0104..3e5809d 100644 --- a/Code/Light.GuardClauses/Exceptions/ExistingItemException.cs +++ b/Code/Light.GuardClauses/Exceptions/ExistingItemException.cs @@ -16,7 +16,7 @@ public class ExistingItemException : CollectionException /// The message of the exception (optional). public ExistingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected ExistingItemException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/InvalidCollectionCountException.cs b/Code/Light.GuardClauses/Exceptions/InvalidCollectionCountException.cs index 70835f3..95bd5be 100644 --- a/Code/Light.GuardClauses/Exceptions/InvalidCollectionCountException.cs +++ b/Code/Light.GuardClauses/Exceptions/InvalidCollectionCountException.cs @@ -16,7 +16,7 @@ public class InvalidCollectionCountException : CollectionException /// The message of the exception (optional). public InvalidCollectionCountException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected InvalidCollectionCountException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/InvalidConfigurationException.cs b/Code/Light.GuardClauses/Exceptions/InvalidConfigurationException.cs index 110f48d..4883a8f 100644 --- a/Code/Light.GuardClauses/Exceptions/InvalidConfigurationException.cs +++ b/Code/Light.GuardClauses/Exceptions/InvalidConfigurationException.cs @@ -16,7 +16,7 @@ public class InvalidConfigurationException : Exception /// The exception that is the cause of this one (optional). public InvalidConfigurationException(string? message = null, Exception? innerException = null) : base(message, innerException) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected InvalidConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/InvalidDateTimeException.cs b/Code/Light.GuardClauses/Exceptions/InvalidDateTimeException.cs index 867f5ae..28605dc 100644 --- a/Code/Light.GuardClauses/Exceptions/InvalidDateTimeException.cs +++ b/Code/Light.GuardClauses/Exceptions/InvalidDateTimeException.cs @@ -16,7 +16,7 @@ public class InvalidDateTimeException : ArgumentException /// The message of the exception (optional). public InvalidDateTimeException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected InvalidDateTimeException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/InvalidEmailAddressException.cs b/Code/Light.GuardClauses/Exceptions/InvalidEmailAddressException.cs index bef57a3..23dff0c 100644 --- a/Code/Light.GuardClauses/Exceptions/InvalidEmailAddressException.cs +++ b/Code/Light.GuardClauses/Exceptions/InvalidEmailAddressException.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Runtime.Serialization; namespace Light.GuardClauses.Exceptions; @@ -16,7 +16,7 @@ public class InvalidEmailAddressException : StringException /// The message of the exception (optional). public InvalidEmailAddressException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected InvalidEmailAddressException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/InvalidStateException.cs b/Code/Light.GuardClauses/Exceptions/InvalidStateException.cs index 9556d50..e6a86dd 100644 --- a/Code/Light.GuardClauses/Exceptions/InvalidStateException.cs +++ b/Code/Light.GuardClauses/Exceptions/InvalidStateException.cs @@ -16,7 +16,7 @@ public class InvalidStateException : Exception /// The exception that is the cause of this one (optional). public InvalidStateException(string? message = null, Exception? innerException = null) : base(message, innerException) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/InvalidUriSchemeException.cs b/Code/Light.GuardClauses/Exceptions/InvalidUriSchemeException.cs index 60a5b5a..7504457 100644 --- a/Code/Light.GuardClauses/Exceptions/InvalidUriSchemeException.cs +++ b/Code/Light.GuardClauses/Exceptions/InvalidUriSchemeException.cs @@ -16,7 +16,7 @@ public class InvalidUriSchemeException : UriException /// The message of the exception (optional). public InvalidUriSchemeException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected InvalidUriSchemeException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/MissingItemException.cs b/Code/Light.GuardClauses/Exceptions/MissingItemException.cs index b0c8358..78ab68b 100644 --- a/Code/Light.GuardClauses/Exceptions/MissingItemException.cs +++ b/Code/Light.GuardClauses/Exceptions/MissingItemException.cs @@ -16,7 +16,7 @@ public class MissingItemException : CollectionException /// The message of the exception (optional). public MissingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected MissingItemException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/NullableHasNoValueException.cs b/Code/Light.GuardClauses/Exceptions/NullableHasNoValueException.cs index 23cb4d9..4dc1ea7 100644 --- a/Code/Light.GuardClauses/Exceptions/NullableHasNoValueException.cs +++ b/Code/Light.GuardClauses/Exceptions/NullableHasNoValueException.cs @@ -16,7 +16,7 @@ public class NullableHasNoValueException : ArgumentException /// The message of the exception (optional). public NullableHasNoValueException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected NullableHasNoValueException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/RelativeUriException.cs b/Code/Light.GuardClauses/Exceptions/RelativeUriException.cs index f8d2a9d..0e57847 100644 --- a/Code/Light.GuardClauses/Exceptions/RelativeUriException.cs +++ b/Code/Light.GuardClauses/Exceptions/RelativeUriException.cs @@ -16,7 +16,7 @@ public class RelativeUriException : UriException /// The message of the exception (optional). public RelativeUriException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected RelativeUriException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/SameObjectReferenceException.cs b/Code/Light.GuardClauses/Exceptions/SameObjectReferenceException.cs index 378d57c..82a8ec7 100644 --- a/Code/Light.GuardClauses/Exceptions/SameObjectReferenceException.cs +++ b/Code/Light.GuardClauses/Exceptions/SameObjectReferenceException.cs @@ -16,7 +16,7 @@ public class SameObjectReferenceException : ArgumentException /// The message of the exception (optional). public SameObjectReferenceException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected SameObjectReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/StringDoesNotMatchException.cs b/Code/Light.GuardClauses/Exceptions/StringDoesNotMatchException.cs index c7fc2c0..684dc6c 100644 --- a/Code/Light.GuardClauses/Exceptions/StringDoesNotMatchException.cs +++ b/Code/Light.GuardClauses/Exceptions/StringDoesNotMatchException.cs @@ -16,7 +16,7 @@ public class StringDoesNotMatchException : StringException /// The message of the exception (optional). public StringDoesNotMatchException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected StringDoesNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/StringException.cs b/Code/Light.GuardClauses/Exceptions/StringException.cs index 9e3d42f..f76d72c 100644 --- a/Code/Light.GuardClauses/Exceptions/StringException.cs +++ b/Code/Light.GuardClauses/Exceptions/StringException.cs @@ -16,7 +16,7 @@ public class StringException : ArgumentException /// The message of the exception (optional). public StringException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected StringException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/StringLengthException.cs b/Code/Light.GuardClauses/Exceptions/StringLengthException.cs index f728b59..ed34616 100644 --- a/Code/Light.GuardClauses/Exceptions/StringLengthException.cs +++ b/Code/Light.GuardClauses/Exceptions/StringLengthException.cs @@ -16,7 +16,7 @@ public class StringLengthException : StringException /// The message of the exception (optional). public StringLengthException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected StringLengthException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/SubstringException.cs b/Code/Light.GuardClauses/Exceptions/SubstringException.cs index 862dbe5..310d6cc 100644 --- a/Code/Light.GuardClauses/Exceptions/SubstringException.cs +++ b/Code/Light.GuardClauses/Exceptions/SubstringException.cs @@ -16,7 +16,7 @@ public class SubstringException : StringException /// The message of the exception (optional). public SubstringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected SubstringException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/TypeCastException.cs b/Code/Light.GuardClauses/Exceptions/TypeCastException.cs index acf5b88..eac4439 100644 --- a/Code/Light.GuardClauses/Exceptions/TypeCastException.cs +++ b/Code/Light.GuardClauses/Exceptions/TypeCastException.cs @@ -16,7 +16,7 @@ public class TypeCastException : ArgumentException /// The message of the exception (optional). public TypeCastException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected TypeCastException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/UriException.cs b/Code/Light.GuardClauses/Exceptions/UriException.cs index 8486426..ec95efe 100644 --- a/Code/Light.GuardClauses/Exceptions/UriException.cs +++ b/Code/Light.GuardClauses/Exceptions/UriException.cs @@ -16,7 +16,7 @@ public class UriException : ArgumentException /// The message of the exception (optional). public UriException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected UriException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/ValueIsNotOneOfException.cs b/Code/Light.GuardClauses/Exceptions/ValueIsNotOneOfException.cs index 61f60d4..f70bcfb 100644 --- a/Code/Light.GuardClauses/Exceptions/ValueIsNotOneOfException.cs +++ b/Code/Light.GuardClauses/Exceptions/ValueIsNotOneOfException.cs @@ -16,7 +16,7 @@ public class ValueIsNotOneOfException : ArgumentException /// The message of the exception (optional). public ValueIsNotOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected ValueIsNotOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/ValueIsOneOfException.cs b/Code/Light.GuardClauses/Exceptions/ValueIsOneOfException.cs index 45b1f4e..25c19ef 100644 --- a/Code/Light.GuardClauses/Exceptions/ValueIsOneOfException.cs +++ b/Code/Light.GuardClauses/Exceptions/ValueIsOneOfException.cs @@ -16,7 +16,7 @@ public class ValueIsOneOfException : ArgumentException /// The message of the exception (optional). public ValueIsOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected ValueIsOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/ValuesEqualException.cs b/Code/Light.GuardClauses/Exceptions/ValuesEqualException.cs index 0ec64a2..2a059bd 100644 --- a/Code/Light.GuardClauses/Exceptions/ValuesEqualException.cs +++ b/Code/Light.GuardClauses/Exceptions/ValuesEqualException.cs @@ -16,7 +16,7 @@ public class ValuesEqualException : ArgumentException /// The message of the exception (optional). public ValuesEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected ValuesEqualException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/ValuesNotEqualException.cs b/Code/Light.GuardClauses/Exceptions/ValuesNotEqualException.cs index 5c0bd89..22da3b4 100644 --- a/Code/Light.GuardClauses/Exceptions/ValuesNotEqualException.cs +++ b/Code/Light.GuardClauses/Exceptions/ValuesNotEqualException.cs @@ -16,7 +16,7 @@ public class ValuesNotEqualException : ArgumentException /// The message of the exception (optional). public ValuesNotEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected ValuesNotEqualException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/Exceptions/WhiteSpaceStringException.cs b/Code/Light.GuardClauses/Exceptions/WhiteSpaceStringException.cs index ea6700b..6a01ead 100644 --- a/Code/Light.GuardClauses/Exceptions/WhiteSpaceStringException.cs +++ b/Code/Light.GuardClauses/Exceptions/WhiteSpaceStringException.cs @@ -16,7 +16,7 @@ public class WhiteSpaceStringException : StringException /// The message of the exception (optional). public WhiteSpaceStringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected WhiteSpaceStringException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif diff --git a/Code/Light.GuardClauses/RegularExpressions.cs b/Code/Light.GuardClauses/RegularExpressions.cs index 238f17c..dad138f 100644 --- a/Code/Light.GuardClauses/RegularExpressions.cs +++ b/Code/Light.GuardClauses/RegularExpressions.cs @@ -5,7 +5,7 @@ namespace Light.GuardClauses; /// /// Provides regular expressions that are used in string assertions. /// -#if NET8_0 +#if NET8_0_OR_GREATER public static partial class RegularExpressions #else public static class RegularExpressions @@ -24,7 +24,7 @@ public static class RegularExpressions /// was modified to satisfy all tests of https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/. /// public static readonly Regex EmailRegex = -#if NET8_0 +#if NET8_0_OR_GREATER GenerateEmailRegex(); [GeneratedRegex(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant)] From 3cffc0718a1f59cf31e90d0c97005d76789fbe6b Mon Sep 17 00:00:00 2001 From: Chris Donnelly Date: Mon, 24 Nov 2025 18:51:29 -0600 Subject: [PATCH 03/10] chore: Move files that contain framework-extension code The files moved need to live in the Light.GuardClauses.FrameworkExtensions namespace, and not the standard Light.GuardClauses namespace. This is so other polyfill libraries like https://github.com/SimonCropp/Polyfill can be used with Light.GuardClauses and not create conflicts. --- .../ContainsBenchmark.cs | 0 .../StringExtensions.Contains.cs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename Code/Light.GuardClauses.Performance/{StringAssertions => FrameworkExtensions}/ContainsBenchmark.cs (100%) rename Code/Light.GuardClauses/{Check.Contains.cs => FrameworkExtensions/StringExtensions.Contains.cs} (100%) diff --git a/Code/Light.GuardClauses.Performance/StringAssertions/ContainsBenchmark.cs b/Code/Light.GuardClauses.Performance/FrameworkExtensions/ContainsBenchmark.cs similarity index 100% rename from Code/Light.GuardClauses.Performance/StringAssertions/ContainsBenchmark.cs rename to Code/Light.GuardClauses.Performance/FrameworkExtensions/ContainsBenchmark.cs diff --git a/Code/Light.GuardClauses/Check.Contains.cs b/Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.Contains.cs similarity index 100% rename from Code/Light.GuardClauses/Check.Contains.cs rename to Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.Contains.cs From 21af1abad5b7076f3bda1353cee1602e13f95477 Mon Sep 17 00:00:00 2001 From: Chris Donnelly Date: Mon, 24 Nov 2025 18:54:37 -0600 Subject: [PATCH 04/10] chore: Modify moved files to new class/namespace This modifies the Contains extension methods to move it to a new namespace. Note this commit is separate from the rename operation in order to minimize the chance that Git loses file history tracking. --- .../FrameworkExtensions/ContainsBenchmark.cs | 2 +- .../FrameworkExtensions/StringExtensions.Contains.cs | 4 ++-- .../FrameworkExtensions/StringExtensions.cs | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.cs diff --git a/Code/Light.GuardClauses.Performance/FrameworkExtensions/ContainsBenchmark.cs b/Code/Light.GuardClauses.Performance/FrameworkExtensions/ContainsBenchmark.cs index 55b4b89..32c9c29 100644 --- a/Code/Light.GuardClauses.Performance/FrameworkExtensions/ContainsBenchmark.cs +++ b/Code/Light.GuardClauses.Performance/FrameworkExtensions/ContainsBenchmark.cs @@ -1,7 +1,7 @@ using System; using BenchmarkDotNet.Attributes; -namespace Light.GuardClauses.Performance.StringAssertions +namespace Light.GuardClauses.FrameworkExtensions.Performance.StringAssertions { public class ContainsOrdinalBenchmark { diff --git a/Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.Contains.cs b/Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.Contains.cs index 5e5c9f2..703e9be 100644 --- a/Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.Contains.cs +++ b/Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.Contains.cs @@ -3,9 +3,9 @@ using JetBrains.Annotations; using NotNullAttribute = System.Diagnostics.CodeAnalysis.NotNullAttribute; -namespace Light.GuardClauses; +namespace Light.GuardClauses.FrameworkExtensions; -public static partial class Check +public static partial class StringExtensions { /// /// Checks if the string contains the specified value using the given comparison type. diff --git a/Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.cs b/Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.cs new file mode 100644 index 0000000..d631d79 --- /dev/null +++ b/Code/Light.GuardClauses/FrameworkExtensions/StringExtensions.cs @@ -0,0 +1,7 @@ +namespace Light.GuardClauses.FrameworkExtensions; + +/// +/// Provides extension methods for the class. +/// +// ReSharper disable once RedundantTypeDeclarationBody -- required for Source Code Transformation +public static partial class StringExtensions { } From c74190329f1d14a79502d1851e34e2759e679bc9 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 26 Nov 2025 05:21:53 +0100 Subject: [PATCH 05/10] chore: introduce new SolutionItems solution folder Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses.AllProjects.sln | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/Code/Light.GuardClauses.AllProjects.sln b/Code/Light.GuardClauses.AllProjects.sln index 461effb..474b424 100644 --- a/Code/Light.GuardClauses.AllProjects.sln +++ b/Code/Light.GuardClauses.AllProjects.sln @@ -23,11 +23,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.SourceCo EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.Source", "Light.GuardClauses.Source\Light.GuardClauses.Source.csproj", "{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Versioning", "Versioning", "{176BEDC4-CFE3-4084-8213-B4EC155A7E38}" - ProjectSection(SolutionItems) = preProject - Version.props = Version.props - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.SourceCodeTransformation.Tests", "Light.GuardClauses.SourceCodeTransformation.Tests\Light.GuardClauses.SourceCodeTransformation.Tests.csproj", "{A2067796-F167-47BE-B367-191152DCE230}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plans", "Plans", "{664661A0-3861-486B-87B5-A35A5CC5F7B7}" @@ -35,6 +30,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plans", "Plans", "{664661A0 Plans\issue-114-must-not-be-default-or-empty-for-immutable-array.md = Plans\issue-114-must-not-be-default-or-empty-for-immutable-array.md EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{8A42A536-E597-454B-BE18-4F62311FA158}" + ProjectSection(SolutionItems) = preProject + ..\CONTRIBUTING.md = ..\CONTRIBUTING.md + ..\README.md = ..\README.md + ..\LICENSE = ..\LICENSE + Directory.Packages.props = Directory.Packages.props + Version.props = Version.props + Light.GuardClauses.AllProjects.sln.DotSettings = Light.GuardClauses.AllProjects.sln.DotSettings + Light.GuardClauses.sln.DotSettings = Light.GuardClauses.sln.DotSettings + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU From 7b9b3c9388db4a0eedc1a212d00c529c3e008729 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 26 Nov 2025 05:23:23 +0100 Subject: [PATCH 06/10] build: update dependencies, introduce Central Package Management Signed-off-by: Kenny Pflug --- Code/Directory.Packages.props | 20 +++++++++++ ...auses.InternalRoslynAnalyzers.Tests.csproj | 10 +++--- ...uardClauses.InternalRoslynAnalyzers.csproj | 4 +-- .../Light.GuardClauses.Performance.csproj | 4 +-- .../Light.GuardClauses.Source.csproj | 5 ++- ...uses.SourceCodeTransformation.Tests.csproj | 14 ++++---- ...ardClauses.SourceCodeTransformation.csproj | 10 +++--- .../Light.GuardClauses.Tests.csproj | 33 +++++++++---------- .../Light.GuardClauses.csproj | 8 ++--- 9 files changed, 61 insertions(+), 47 deletions(-) create mode 100644 Code/Directory.Packages.props diff --git a/Code/Directory.Packages.props b/Code/Directory.Packages.props new file mode 100644 index 0000000..a2f4fe0 --- /dev/null +++ b/Code/Directory.Packages.props @@ -0,0 +1,20 @@ + + + true + false + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Code/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj b/Code/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj index c15326f..249d20e 100644 --- a/Code/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj +++ b/Code/Light.GuardClauses.InternalRoslynAnalyzers.Tests/Light.GuardClauses.InternalRoslynAnalyzers.Tests.csproj @@ -1,4 +1,4 @@ - + net8.0 @@ -6,10 +6,10 @@ - - - - + + + + diff --git a/Code/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj b/Code/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj index 1df3b66..83d7c0a 100644 --- a/Code/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj +++ b/Code/Light.GuardClauses.InternalRoslynAnalyzers/Light.GuardClauses.InternalRoslynAnalyzers.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -6,7 +6,7 @@ - + \ No newline at end of file diff --git a/Code/Light.GuardClauses.Performance/Light.GuardClauses.Performance.csproj b/Code/Light.GuardClauses.Performance/Light.GuardClauses.Performance.csproj index cbf1418..30ed96d 100644 --- a/Code/Light.GuardClauses.Performance/Light.GuardClauses.Performance.csproj +++ b/Code/Light.GuardClauses.Performance/Light.GuardClauses.Performance.csproj @@ -1,4 +1,4 @@ - + Exe @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj b/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj index 0f9b090..26e356d 100644 --- a/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj +++ b/Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 @@ -10,8 +10,7 @@ - - + \ No newline at end of file diff --git a/Code/Light.GuardClauses.SourceCodeTransformation.Tests/Light.GuardClauses.SourceCodeTransformation.Tests.csproj b/Code/Light.GuardClauses.SourceCodeTransformation.Tests/Light.GuardClauses.SourceCodeTransformation.Tests.csproj index 75dd49f..7b64377 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation.Tests/Light.GuardClauses.SourceCodeTransformation.Tests.csproj +++ b/Code/Light.GuardClauses.SourceCodeTransformation.Tests/Light.GuardClauses.SourceCodeTransformation.Tests.csproj @@ -1,16 +1,16 @@ - + - net6.0 + net8.0 false - - - - - + + + + + \ No newline at end of file diff --git a/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj b/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj index 371827b..d818386 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj +++ b/Code/Light.GuardClauses.SourceCodeTransformation/Light.GuardClauses.SourceCodeTransformation.csproj @@ -1,4 +1,4 @@ - + @@ -10,10 +10,10 @@ - - - - + + + + diff --git a/Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj b/Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj index 34f0e4e..9439778 100644 --- a/Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj +++ b/Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj @@ -1,23 +1,20 @@ - + - + - - net8.0 - false - 12 - true - + + net8.0 + false + 12 + true + - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + + + + \ No newline at end of file diff --git a/Code/Light.GuardClauses/Light.GuardClauses.csproj b/Code/Light.GuardClauses/Light.GuardClauses.csproj index 845a5fe..6220446 100644 --- a/Code/Light.GuardClauses/Light.GuardClauses.csproj +++ b/Code/Light.GuardClauses/Light.GuardClauses.csproj @@ -35,11 +35,9 @@ - - - - - + + + From cdb745cf5ff58d8b6c9469df19cf8bbcf407d2f8 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 26 Nov 2025 05:23:40 +0100 Subject: [PATCH 07/10] test: cleanup ParseCSharpFilesWithRoslynTests.cs Signed-off-by: Kenny Pflug --- .../ParseCSharpFilesWithRoslynTests.cs | 82 ++++++++++--------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/Code/Light.GuardClauses.SourceCodeTransformation.Tests/ParseCSharpFilesWithRoslynTests.cs b/Code/Light.GuardClauses.SourceCodeTransformation.Tests/ParseCSharpFilesWithRoslynTests.cs index 00f0f8f..9462f94 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation.Tests/ParseCSharpFilesWithRoslynTests.cs +++ b/Code/Light.GuardClauses.SourceCodeTransformation.Tests/ParseCSharpFilesWithRoslynTests.cs @@ -5,54 +5,56 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Xunit; -namespace Light.GuardClauses.SourceCodeTransformation.Tests +namespace Light.GuardClauses.SourceCodeTransformation.Tests; + +public static class ParseCSharpWithRoslynTests { - public static class ParseCSharpWithRoslynTests - { - private static readonly DirectoryInfo CodeDirectory; + private static readonly DirectoryInfo CodeDirectory; - static ParseCSharpWithRoslynTests() - { - CodeDirectory = FindCodeDirectory(); - } + static ParseCSharpWithRoslynTests() => CodeDirectory = FindCodeDirectory(); - [Fact] - public static void ParseExpressionExtensionsFile() - { - var fileInfo = GetLightGuardClausesFile(@"FrameworkExtensions\ExpressionExtensions.cs"); - var syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(fileInfo.FullName)); - var root = (CompilationUnitSyntax) syntaxTree.GetRoot(); - - root.Members.Should().NotBeEmpty(); - } + [Fact] + public static void ParseExpressionExtensionsFile() + { + var fileInfo = GetLightGuardClausesFile("FrameworkExtensions/ExpressionExtensions.cs"); + var syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(fileInfo.FullName)); + var root = (CompilationUnitSyntax) syntaxTree.GetRoot(); - [Fact] - public static void ParseSpanDelegatesFile() - { - var fileInfo = GetLightGuardClausesFile(@"ExceptionFactory\SpanDelegates.cs"); - var syntaxTree = CSharpSyntaxTree.ParseText(File.ReadAllText(fileInfo.FullName), new CSharpParseOptions(LanguageVersion.CSharp7_3, preprocessorSymbols: new[] { "NETSTANDARD2_0" })); - var root = (CompilationUnitSyntax) syntaxTree.GetRoot(); + root.Members.Should().NotBeEmpty(); + } - root.Members.Should().NotBeEmpty(); - } + [Fact] + public static void ParseSpanDelegatesFile() + { + var fileInfo = GetLightGuardClausesFile("ExceptionFactory/SpanDelegates.cs"); + var syntaxTree = CSharpSyntaxTree.ParseText( + File.ReadAllText(fileInfo.FullName), + new (LanguageVersion.CSharp7_3, preprocessorSymbols: ["NETSTANDARD2_0"]) + ); + var root = (CompilationUnitSyntax) syntaxTree.GetRoot(); + + root.Members.Should().NotBeEmpty(); + } - private static DirectoryInfo FindCodeDirectory() + private static DirectoryInfo FindCodeDirectory() + { + var currentDirectory = new DirectoryInfo("."); + do { - var currentDirectory = new DirectoryInfo("."); - do + if (currentDirectory.Name == "Code") { - if (currentDirectory.Name == "Code") - return currentDirectory; + return currentDirectory; + } - currentDirectory = currentDirectory.Parent; - } while (currentDirectory != null); + currentDirectory = currentDirectory.Parent; + } while (currentDirectory != null); - throw new InvalidOperationException("This test project does not reside in a folder called \"Code\" (directly or indirectly)."); - } - - private static FileInfo GetLightGuardClausesFile(string relativeFilePath) - { - return new FileInfo(Path.Combine(CodeDirectory.FullName, "Light.GuardClauses", relativeFilePath)); - } + throw new InvalidOperationException( + "This test project does not reside in a folder called \"Code\" (directly or indirectly)." + ); } -} \ No newline at end of file + + private static FileInfo GetLightGuardClausesFile(string relativeFilePath) => new ( + Path.Combine(CodeDirectory.FullName, "Light.GuardClauses", relativeFilePath) + ); +} From 1251e7f38c46c0b731b1fe2f05e350f48f5678a7 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 26 Nov 2025 05:24:08 +0100 Subject: [PATCH 08/10] chore: add missing using System.Collections.Immutable; to SourceFileMerger.cs Signed-off-by: Kenny Pflug --- .../SourceFileMerger.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs b/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs index a5af7e0..6b162b9 100644 --- a/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs +++ b/Code/Light.GuardClauses.SourceCodeTransformation/SourceFileMerger.cs @@ -61,6 +61,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; From a9459edddecf8b9d3a1bee32a9069475fdc57a90 Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 26 Nov 2025 05:32:46 +0100 Subject: [PATCH 09/10] chore: update readme and PackageReleaseNotes for 14.0.0 release Signed-off-by: Kenny Pflug --- Code/Light.GuardClauses/Light.GuardClauses.csproj | 5 ++--- Code/Version.props | 2 +- README.md | 9 ++++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Code/Light.GuardClauses/Light.GuardClauses.csproj b/Code/Light.GuardClauses/Light.GuardClauses.csproj index 6220446..06bd149 100644 --- a/Code/Light.GuardClauses/Light.GuardClauses.csproj +++ b/Code/Light.GuardClauses/Light.GuardClauses.csproj @@ -26,11 +26,10 @@ true README.md - Light.GuardClauses 13.1.0 + Light.GuardClauses 14.0.0 -------------------------------- - - new assertions for ImmutableArray<T>: MustNotBeDefaultOrEmpty, MustHaveLength, MustHaveLengthIn, MustHaveMinimumLength, MustHaveMaximumLength - - new dependency: System.Collections.Immutable + - Breaking Change: Check.Contains(string, string, System.StringComparison) now exists in Light.GuardClauses.FrameworkExtensions to avoid conflict with other polyfill libraries diff --git a/Code/Version.props b/Code/Version.props index e4e99d8..199192b 100644 --- a/Code/Version.props +++ b/Code/Version.props @@ -1,5 +1,5 @@  - 13.1.0 + 14.0.0 \ No newline at end of file diff --git a/README.md b/README.md index d4062b7..ae1e123 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ **A lightweight .NET library for expressive Guard Clauses.** [![License](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/LICENSE) -[![NuGet](https://img.shields.io/badge/NuGet-13.1.0-blue.svg?style=for-the-badge)](https://www.nuget.org/packages/Light.GuardClauses/) -[![Source Code](https://img.shields.io/badge/Source%20Code-13.1.0-blue.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/Light.GuardClauses.SingleFile.cs) +[![NuGet](https://img.shields.io/badge/NuGet-14.0.0-blue.svg?style=for-the-badge)](https://www.nuget.org/packages/Light.GuardClauses/) +[![Source Code](https://img.shields.io/badge/Source%20Code-14.0.0-blue.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/blob/master/Light.GuardClauses.SingleFile.cs) [![Documentation](https://img.shields.io/badge/Docs-Wiki-yellowgreen.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/wiki) [![Documentation](https://img.shields.io/badge/Docs-Changelog-yellowgreen.svg?style=for-the-badge)](https://github.com/feO2x/Light.GuardClauses/releases) @@ -108,7 +108,10 @@ And, of course, the functional correctness of **Light.GuardClauses** is covered ## Supported Platforms -**Light.GuardClauses** is built against [.NET 8, .NET Standard 2.0 and 2.1](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), thus it can be used with a large variaty of target frameworks like .NET 5 or newer .NET Framework 4.6.1 or newer, Unity, Mono, or UWP. +**Light.GuardClauses** is built +against [.NET 8, .NET Standard 2.0 and 2.1](https://docs.microsoft.com/en-us/dotnet/standard/net-standard), thus it can +be used with a large variety of target frameworks like .NET 5 or newer .NET Framework 4.6.1 or newer, Unity, Mono, or +UWP. ## How to Install From c8345d315ae8a96193a946c888b05cf2f8291fde Mon Sep 17 00:00:00 2001 From: Kenny Pflug Date: Wed, 26 Nov 2025 05:36:49 +0100 Subject: [PATCH 10/10] chore: update Single file to Light.GuardClauses 14.0.0 version Signed-off-by: Kenny Pflug --- Light.GuardClauses.SingleFile.cs | 8529 +++++++++++++++--------------- 1 file changed, 4271 insertions(+), 4258 deletions(-) diff --git a/Light.GuardClauses.SingleFile.cs b/Light.GuardClauses.SingleFile.cs index 758820c..ec5c004 100644 --- a/Light.GuardClauses.SingleFile.cs +++ b/Light.GuardClauses.SingleFile.cs @@ -1,5 +1,5 @@ /* ------------------------------ - Light.GuardClauses 13.1.0 + Light.GuardClauses 14.0.0 ------------------------------ License information for Light.GuardClauses @@ -28,6 +28,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -53,635 +54,531 @@ namespace Light.GuardClauses internal static class Check { /// - /// Ensures that the specified URI is an absolute one, or otherwise throws a . + /// Ensures that the string ends with the specified value, or otherwise throws a . /// - /// The URI to be checked. + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not an absolute URI. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// Thrown when does not end with . + /// Thrown when or is null. + /// Thrown when is not a valid value. + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) + if (!parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) { - Throw.MustBeAbsoluteUri(parameter, parameterName, message); + Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); } return parameter; } /// - /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. + /// Ensures that the string ends with the specified value, or otherwise throws a . /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// Your custom exception thrown when is not an absolute URI, or when is null. + /// The string to be checked. + /// The other string must end with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (parameter is null || parameter.IsAbsoluteUri == false) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, value!); } return parameter; } /// - /// Checks if the given is one of the specified . + /// Ensures that the string ends with the specified value, or otherwise throws a . /// - /// The item to be checked. - /// The collection that might contain the . - /// Thrown when is null. + /// The string to be checked. + /// The other string must end with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not end with , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - public static bool IsOneOf(this TItem item, [NotNull][ValidatedNotNull] IEnumerable items) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (items is ICollection collection) - { - return collection.Contains(item); - } - - if (items is string @string && item is char character) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) { - return @string.IndexOf(character) != -1; + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } - return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); + return parameter; } /// - /// Ensures that the collection does not contain the specified item, or otherwise throws an . + /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws an . /// - /// The collection to be checked. - /// The item that must not be part of the collection. + /// The URI to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . + /// Thrown when uses a different scheme than "http" or "https". + /// Thrown when is relative and thus has no scheme. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static Uri MustBeHttpOrHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is ICollection collection) - { - if (collection.Contains(item)) - { - Throw.ExistingItem(parameter, item, parameterName, message); - } - - return parameter; - } - - if (parameter.MustNotBeNull(parameterName, message).Contains(item)) + if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) { - Throw.ExistingItem(parameter, item, parameterName, message); + Throw.UriMustHaveOneSchemeOf(parameter, ["https", "http"], parameterName, message); } return parameter; } /// - /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. + /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The item that must not be part of the collection. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when contains . + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "http" or "https", or when is a relative URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) - where TCollection : class, IEnumerable + public static Uri MustBeHttpOrHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - if (parameter is ICollection collection) - { - if (collection.Contains(item)) - { - Throw.CustomException(exceptionFactory, parameter, item); - } - - return parameter; - } - - if (parameter is null || parameter.Contains(item)) + if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) { - Throw.CustomException(exceptionFactory, parameter, item); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the string does not contain the specified value, or otherwise throws a . + /// Ensures that is within the specified range, or otherwise throws an . /// - /// The string to be checked. - /// The string that must not be part of . + /// The type of the parameter to be checked. + /// The parameter to be checked. + /// The range where must be in-between. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. + /// Thrown when is not within . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) { - Throw.StringContains(parameter, value, parameterName, message); + Throw.MustBeInRange(parameter, range, parameterName, message); } return parameter; } /// - /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. + /// Ensures that is within the specified range, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The string that must not be part of . - /// The delegate that creates your custom exception (optional). and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// + /// The parameter to be checked. + /// The range where must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not within , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) + where T : IComparable { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.Contains(value)) + if (parameter is null || !range.IsValueWithinRange(parameter)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter!, range); } return parameter; } /// - /// Ensures that the string does not contain the specified value, or otherwise throws a . + /// Checks if the specified type derives from the other type. Internally, this method uses + /// by default so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The string to be checked. - /// The string that must not be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// The type info to be checked. + /// The base class that should derive from. + /// Thrown when or is null. + [ContractAnnotation("type:null => halt; baseClass:null => halt")] + public static bool DerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClass) { - if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) >= 0) + baseClass.MustNotBeNull(nameof(baseClass)); + var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; + while (currentBaseType != null) { - Throw.StringContains(parameter, value, comparisonType, parameterName, message); + if (currentBaseType.IsEquivalentTypeTo(baseClass)) + { + return true; + } + + currentBaseType = currentBaseType.BaseType; } - return parameter; + return false; } /// - /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. + /// Checks if the specified type derives from the other type. This overload uses the specified + /// to compare the types. /// - /// The string to be checked. - /// The string that must not be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception (optional). , , and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + /// The type info to be checked. + /// The base class that should derive from. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. + [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] + public static bool DerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClass, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) >= 0) + baseClass.MustNotBeNull(nameof(baseClass)); + typeComparer.MustNotBeNull(nameof(typeComparer)); + var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; + while (currentBaseType != null) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + if (typeComparer.Equals(currentBaseType, baseClass)) + { + return true; + } + + currentBaseType = currentBaseType.BaseType; } - return parameter; + return false; } /// - /// Ensures that the does not contain the specified item, or otherwise throws an . + /// Checks if the specified character is a letter. /// - /// The to be checked. - /// The item that must not be part of the . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLetter(this char character) => char.IsLetter(character); + /// + /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . + /// + /// The string to be checked. + /// The length that the string must be shorter than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// - /// The default instance of cannot contain any items, so this method will not throw for default instances. - /// + /// Thrown when has a length greater than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustNotContain(this ImmutableArray parameter, T item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsDefault && parameter.Contains(item)) + if (parameter.MustNotBeNull(parameterName, message).Length > length) { - Throw.ExistingItem(parameter, item, parameterName, message); + Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the does not contain the specified item, or otherwise throws your custom exception. + /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws your custom exception. /// - /// The to be checked. - /// The item that must not be part of the . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when contains . - /// - /// The default instance of cannot contain any items, so this method will not throw for default instances. - /// + /// The string to be checked. + /// The length that the string must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length greater than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static ImmutableArray MustNotContain(this ImmutableArray parameter, T item, Func, T, Exception> exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (!parameter.IsDefault && parameter.Contains(item)) + if (parameter is null || parameter.Length > length) { - Throw.CustomException(exceptionFactory, parameter, item); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } /// - /// Checks if the specified string is trimmed at the end, i.e. it does not end with - /// white space characters. Inputting an empty string will return true. - /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed at the end, else false. - /// An empty string will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtEnd(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtEnd(); - /// - /// Checks if the specified character span is trimmed at the end, i.e. it does not end with - /// white space characters. Inputting an empty span will return true. - /// - /// The character span to be checked. - /// - /// True if the is trimmed at the end, else false. - /// An empty span will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[parameter.Length - 1].IsWhiteSpace(); - /// - /// Ensures that the collection has the specified number of items, or otherwise throws an . + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// - /// The collection to be checked. - /// The number of items the collection must have. + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified number of items. - /// Thrown when is null. + /// Thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter!.Count(parameterName, message) != count) - { - Throw.InvalidCollectionCount(parameter, count, parameterName, message); - } - + ((ReadOnlySpan)parameter).MustBeShorterThanOrEqualTo(length, parameterName, message); return parameter; } /// - /// Ensures that the collection has the specified number of items, or otherwise throws your custom exception. + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The number of items the collection must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified number of items, or when is null. + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable + public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter is null || parameter.Count() != count) + if (parameter.Length > length) { - Throw.CustomException(exceptionFactory, parameter, count); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the collection is not null or empty, or otherwise throws an . + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . /// - /// The collection to be checked. + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has no items. - /// Thrown when is null. + /// Thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Count(parameterName, message) == 0) + if (parameter.Length > length) { - Throw.EmptyCollection(parameterName, message); + Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. + /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The delegate that creates your custom exception. - /// Thrown when has no items, or when is null. + /// The span to be checked. + /// The length value that the span must be shorter than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) - where TCollection : class, IEnumerable + public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter is null || parameter.Count() == 0) + if (parameter.Length > length) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified string is not null or empty, or otherwise throws an or . + /// Checks if the specified is true and throws an in this case. /// - /// The string to be checked. + /// The condition to be checked. The exception is thrown when it is true. /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an empty string. - /// Thrown when is null. + /// The message that will be passed to the (optional). + /// Thrown when is true. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static void InvalidArgument(bool condition, string? parameterName = null, string? message = null) { - if (parameter is null) - { - Throw.ArgumentNull(parameterName, message); - } - - if (parameter.Length == 0) + if (condition) { - Throw.EmptyString(parameterName, message); + Throw.Argument(parameterName, message); } - - return parameter; } /// - /// Ensures that the specified string is not null or empty, or otherwise throws your custom exception. + /// Checks if the specified is true and throws your custom exception in this case. /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is an empty string or null. + /// The condition to be checked. The exception is thrown when it is true. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is true. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static string MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static void InvalidArgument(bool condition, Func exceptionFactory) { - if (parameter.IsNullOrEmpty()) + if (condition) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory); } - - return parameter; } /// - /// Ensures that the URI has one of the specified schemes, or otherwise throws an . + /// Checks if the specified is true and throws your custom exception in this case. /// - /// The URI to be checked. - /// One of these strings must be equal to the scheme of the URI. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the scheme is not equal to one of the specified schemes. - /// Thrown when is relative and thus has no scheme. - /// Thrown when or is null. + /// The condition to be checked. The exception is thrown when it is true. + /// The value that is checked in the . This value is passed to the . + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when is true. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; schemes:null => halt")] - public static Uri MustHaveOneSchemeOf([NotNull][ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("exceptionFactory:null => halt")] + public static void InvalidArgument(bool condition, T parameter, Func exceptionFactory) { - // ReSharper disable PossibleMultipleEnumeration - parameter.MustBeAbsoluteUri(parameterName, message); - if (schemes is ICollection collection) - { - if (!collection.Contains(parameter.Scheme)) - { - Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); - } - - return parameter; - } - - if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) + if (condition) { - Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); + Throw.CustomException(exceptionFactory, parameter); } - - return parameter; - // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the URI has one of the specified schemes, or otherwise throws your custom exception. + /// Ensures that is not within the specified range, or otherwise throws an . /// - /// The URI to be checked. - /// One of these strings must be equal to the scheme of the URI. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the scheme is not equal to one of the specified schemes, or when is a relative URI, or when is null. - /// Thrown when is null. - /// The type of the collection containing the schemes. + /// The type of the parameter to be checked. + /// The parameter to be checked. + /// The range where must not be in-between. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is within . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveOneSchemeOf([NotNull][ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) - where TCollection : class, IEnumerable + public static T MustNotBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (parameter is null || !parameter.IsAbsoluteUri) - { - Throw.CustomException(exceptionFactory, parameter, schemes); - } - - if (schemes is ICollection collection) - { - if (!collection.Contains(parameter.Scheme)) - { - Throw.CustomException(exceptionFactory, parameter, schemes); - } - - return parameter; - } - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (schemes is null || !schemes.Contains(parameter.Scheme)) + if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) { - Throw.CustomException(exceptionFactory, parameter, schemes!); + Throw.MustNotBeInRange(parameter, range, parameterName, message); } return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// Ensures that is not within the specified range, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than . - /// Thrown when is null. + /// The parameter to be checked. + /// The range where must not be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is within , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) where T : IComparable { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || range.IsValueWithinRange(parameter)) { - Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + Throw.CustomException(exceptionFactory, parameter!, range); } return parameter; } /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// Checks if the given is a generic type that has open generic parameters, + /// but is no generic type definition. /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than , or when is null. + /// The type to be checked. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) < 0) - { - Throw.CustomException(exceptionFactory, parameter!, other); - } - - return parameter; - } - + [ContractAnnotation("type:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static bool IsOpenConstructedGenericType([NotNull][ValidatedNotNull] this Type type) => type.MustNotBeNull(nameof(type)).IsGenericType && type.ContainsGenericParameters && type.IsGenericTypeDefinition == false; /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws an . + /// Checks if the specified character is a letter or digit. /// - /// The collection to be checked. - /// The number of items the collection should have at most. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLetterOrDigit(this char character) => char.IsLetterOrDigit(character); + /// + /// Checks if the specified string is trimmed at the end, i.e. it does not end with + /// white space characters. Inputting an empty string will return true. + /// + /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed at the end, else false. + /// An empty string will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtEnd(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtEnd(); + /// + /// Checks if the specified character span is trimmed at the end, i.e. it does not end with + /// white space characters. Inputting an empty span will return true. + /// + /// The character span to be checked. + /// + /// True if the is trimmed at the end, else false. + /// An empty span will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtEnd(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[parameter.Length - 1].IsWhiteSpace(); + /// + /// Ensures that the has at most the specified length, or otherwise throws an . + /// + /// The to be checked. + /// The maximum length the should have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain at most the specified number of items. - /// Thrown when is null. + /// Thrown when has more than the specified length. + /// The default instance of will be treated as having length 0. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static ImmutableArray MustHaveMaximumLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Count(parameterName, message) > count) + var parameterLength = parameter.IsDefault ? 0 : parameter.Length; + if (parameterLength > length) { - Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); + Throw.InvalidMaximumImmutableArrayLength(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the collection has at most the specified number of items, or otherwise throws your custom exception. + /// Ensures that the has at most the specified length, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The number of items the collection should have at most. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. + /// The to be checked. + /// The maximum length the should have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when has more than the specified length. + /// The default instance of will be treated as having length 0. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMaximumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable + public static ImmutableArray MustHaveMaximumLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) { - if (parameter is null || parameter.Count() > count) + var parameterLength = parameter.IsDefault ? 0 : parameter.Length; + if (parameterLength > length) { - Throw.CustomException(exceptionFactory, parameter, count); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } /// - /// Checks if the specified character is a digit. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsDigit(this char character) => char.IsDigit(character); - /// - /// Checks if the specified is true and throws an in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The message that will be passed to the . - /// Thrown when is true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidState(bool condition, string? message = null) - { - if (condition) - { - Throw.InvalidState(message); - } - } - - /// - /// Checks if the specified GUID is an empty one. - /// - /// The GUID to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmpty(this Guid parameter) => parameter == Guid.Empty; - /// - /// Ensures that the string is a valid email address using the default email regular expression - /// defined in , or otherwise throws an . + /// Ensures that the specified URI is a relative one, or otherwise throws an . /// - /// The email address that will be validated. + /// The URI to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid email address. + /// Thrown when is an absolute URI. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) { - Throw.InvalidEmailAddress(parameter, parameterName, message); + Throw.MustBeRelativeUri(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string is a valid email address using the default email regular expression - /// defined in , or otherwise throws your custom exception. + /// Ensures that the specified URI is a relative one, or otherwise throws your custom exception. /// - /// The email address that will be validated. + /// The URI to be checked. /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or no valid email address. + /// Your custom exception thrown when is an absolute URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - if (!parameter.IsEmailAddress()) + if (parameter is null || parameter.IsAbsoluteUri) { Throw.CustomException(exceptionFactory, parameter); } @@ -690,178 +587,153 @@ public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? } /// - /// Ensures that the string is a valid email address using the provided regular expression, - /// or otherwise throws an . + /// Ensures that the specified uses , or otherwise throws an . /// - /// The email address that will be validated. - /// The regular expression that determines if the input string is a valid email. + /// The date time to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid email address. - /// Thrown when is null. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static DateTime MustBeLocal(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) + if (parameter.Kind != DateTimeKind.Local) { - Throw.InvalidEmailAddress(parameter, parameterName, message); + Throw.MustBeLocalDateTime(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string is a valid email address using the provided regular expression, - /// or otherwise throws your custom exception. + /// Ensures that the specified uses , or otherwise throws your custom exception. /// - /// The email address that will be validated. - /// The regular expression that determines if the input string is a valid email. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or no valid email address. + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] - public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) + if (parameter.Kind != DateTimeKind.Local) { - Throw.CustomException(exceptionFactory, parameter, emailAddressPattern!); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . + /// Ensures that the string matches the specified regular expression, or otherwise throws a . /// /// The string to be checked. - /// The other string that must not contain . + /// The regular expression used for pattern matching. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. + /// Thrown when does not match the specified regular expression. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; regex:null => halt")] + public static string MustMatch([NotNull][ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) + if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) { - Throw.Substring(parameter, value, parameterName, message); + Throw.StringDoesNotMatch(parameter, regex, parameterName, message); } return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the string matches the specified regular expression, or otherwise throws your custom exception. /// /// The string to be checked. - /// The other string that must not contain . - /// The delegate that creates your custom exception. and are passed to this delegate. + /// The regular expression used for pattern matching. + /// The delegate that creates your custom exception. and are passed to this delegate. /// - /// Your custom exception thrown when contains , + /// Your custom exception thrown when does not match the specified regular expression, /// or when is null, - /// or when is null. + /// or when is null. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + public static string MustMatch([NotNull][ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.Contains(parameter)) + if (parameter is null || regex is null || !regex.IsMatch(parameter)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter, regex!); } return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . + /// Ensures that the specified is not default or empty, or otherwise throws an . /// - /// The string to be checked. - /// The other string that must not contain . - /// One of the enumeration values that specifies the rules for the search. + /// The to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains . - /// Thrown when or is null. - /// Thrown when is not a valid value. + /// Thrown when is default or empty. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - value.MustNotBeNull(nameof(value), message); - parameter.MustNotBeNull(parameterName, message); - if (value.IndexOf(parameter, comparisonType) != -1) + if (parameter.IsDefaultOrEmpty) { - Throw.Substring(parameter, value, comparisonType, parameterName, message); + Throw.EmptyCollection(parameterName, message); } return parameter; } /// - /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the specified is not default or empty, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string that must not contain . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when contains , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. + /// The to be checked. + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when is default or empty. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray parameter, Func, Exception> exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) + if (parameter.IsDefaultOrEmpty) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified is approximately equal to the given + /// Ensures that the specified is greater than or approximately equal to the given /// value, using the default tolerance of 0.0001, or otherwise throws an /// . /// /// The value to be checked. - /// The value that should be approximately equal to. + /// The value that should be greater than or approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when the absolute difference between and is not - /// less than 0.0001. + /// Thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeApproximately(other, 0.0001, parameterName, message); + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeGreaterThanOrApproximately(other, 0.0001, parameterName, message); /// - /// Ensures that the specified is approximately equal to the given + /// Ensures that the specified is greater than or approximately equal to the given /// value, using the default tolerance of 0.0001, or otherwise throws an /// . /// /// The value to be checked. - /// The value that should be approximately equal to. + /// The value that should be greater than or approximately equal to. /// /// The delegate that creates your custom exception. and /// are passed to this delegate. /// /// - /// Thrown when the absolute difference between and is not - /// less than 0.0001. + /// Thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeApproximately(this double parameter, double other, Func exceptionFactory) + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, Func exceptionFactory) { - if (!parameter.IsApproximately(other)) + if (!parameter.IsGreaterThanOrApproximately(other)) { Throw.CustomException(exceptionFactory, parameter, other); } @@ -870,46 +742,46 @@ public static double MustBeApproximately(this double parameter, double other, Fu } /// - /// Ensures that the specified is approximately equal to the given + /// Ensures that the specified is greater than or approximately equal to the given /// value, or otherwise throws an . /// /// The value to be checked. - /// The value that should be approximately equal to. + /// The value that should be greater than or approximately equal to. /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when the absolute difference between and is not - /// less than . + /// Thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!parameter.IsApproximately(other, tolerance)) + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) { - Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the specified is approximately equal to the given + /// Ensures that the specified is greater than or approximately equal to the given /// value, or otherwise throws your custom exception. /// /// The value to be checked. - /// The value that should be approximately equal to. + /// The value that should be greater than or approximately equal to. /// The tolerance indicating how much the two values may differ from each other. - /// The delegate that creates your custom exception. , - /// , and are passed to this delegate. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// /// - /// Your custom exception thrown when the absolute difference between and - /// is not less than . + /// Your custom exception thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) + public static double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - if (!parameter.IsApproximately(other, tolerance)) + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) { Throw.CustomException(exceptionFactory, parameter, other, tolerance); } @@ -918,38 +790,37 @@ public static double MustBeApproximately(this double parameter, double other, do } /// - /// Ensures that the specified is approximately equal to the given + /// Ensures that the specified is greater than or approximately equal to the given /// value, using the default tolerance of 0.0001f, or otherwise throws an /// . /// /// The value to be checked. - /// The value that should be approximately equal to. + /// The value that should be greater than or approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when the absolute difference between and is - /// not less than 0.0001f. + /// Thrown when is not greater than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeApproximately(other, 0.0001f, parameterName, message); + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeGreaterThanOrApproximately(other, 0.0001f, parameterName, message); /// - /// Ensures that the specified is approximately equal to the given + /// Ensures that the specified is greater than or approximately equal to the given /// value, using the default tolerance of 0.0001, or otherwise throws an /// . /// /// The value to be checked. - /// The value that should be approximately equal to. + /// The value that should be greater than or approximately equal to. /// /// The delegate that creates your custom exception. and /// are passed to this delegate. /// /// - /// Thrown when the absolute difference between and is not - /// less than 0.0001. + /// Thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeApproximately(this float parameter, float other, Func exceptionFactory) + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, Func exceptionFactory) { - if (!parameter.IsApproximately(other)) + if (!parameter.IsGreaterThanOrApproximately(other)) { Throw.CustomException(exceptionFactory, parameter, other); } @@ -958,48 +829,46 @@ public static float MustBeApproximately(this float parameter, float other, Func< } /// - /// Ensures that the specified is approximately equal to the given + /// Ensures that the specified is greater than or approximately equal to the given /// value, or otherwise throws an . /// /// The value to be checked. - /// The value that should be approximately equal to. + /// The value that should be greater than or approximately equal to. /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). /// - /// Thrown when the absolute difference between and is not - /// less than . + /// Thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsApproximately(other, tolerance)) + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) { - Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the specified is approximately equal to the given + /// Ensures that the specified is greater than or approximately equal to the given /// value, or otherwise throws your custom exception. /// /// The value to be checked. - /// The value that should be approximately equal to. + /// The value that should be greater than or approximately equal to. /// The tolerance indicating how much the two values may differ from each other. /// - /// The delegate that creates your custom exception. , , and - /// are passed to this delegate. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. /// /// - /// Your custom exception thrown when the absolute difference between and - /// is not less than . + /// Your custom exception thrown when is not greater than or approximately equal to . /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) + public static float MustBeGreaterThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - if (!parameter.IsApproximately(other, tolerance)) + if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) { Throw.CustomException(exceptionFactory, parameter, other, tolerance); } @@ -1008,344 +877,389 @@ public static float MustBeApproximately(this float parameter, float other, float } /// - /// Ensures that the string ends with the specified value, or otherwise throws a . + /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. /// - /// The string to be checked. - /// The other string must end with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is greater than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this double value, double other, double tolerance) => value > other || value.IsApproximately(other, tolerance); + /// + /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is greater than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this double value, double other) => value > other || value.IsApproximately(other); + /// + /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. + /// + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is greater than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this float value, float other, float tolerance) => value > other || value.IsApproximately(other, tolerance); + /// + /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001f. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is greater than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsGreaterThanOrApproximately(this float value, float other) => value > other || value.IsApproximately(other); + /// + /// Ensures that the specified is less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not end with . - /// Thrown when or is null. - /// Thrown when is not a valid value. - public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// Thrown when the specified is not less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (!parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) { - Throw.StringDoesNotEndWith(parameter, value, comparisonType, parameterName, message); + Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string ends with the specified value, or otherwise throws a . + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string must end with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not end with , - /// or when is null, - /// or when is null. - /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is not less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !parameter.EndsWith(value)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) >= 0) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter!, other); } return parameter; } /// - /// Ensures that the string ends with the specified value, or otherwise throws a . + /// Ensures that the value is not one of the specified items, or otherwise throws a . /// - /// The string to be checked. - /// The other string must end with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not end with , - /// or when is null, - /// or when is null. - /// + /// The value to be checked. + /// The items that must not contain the value. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is equal to one of the specified . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static TItem MustNotBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || !parameter.EndsWith(value, comparisonType)) + // ReSharper disable PossibleMultipleEnumeration + if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.ValueIsOneOf(parameter, items, parameterName, message); } return parameter; + // ReSharper restore PossibleMultipleEnumeration } /// - /// Checks if the string contains the specified value using the given comparison type. - /// - /// The string to be checked. - /// The other string. - /// One of the enumeration values that specifies the rules for the search. - /// True if contains , else false. - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("string:null => halt; value:null => halt")] - public static bool Contains(// ReSharper disable once RedundantNullableFlowAttribute -- Caller might have NRTs turned off - [NotNull][ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; - /// - /// Ensures that and do not point to the same object instance, or otherwise - /// throws a . + /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. /// - /// The first reference to be checked. - /// The second reference to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when both and point to the same object. + /// The value to be checked. + /// The items that must not contain the value. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is equal to one of the specified , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : class + [ContractAnnotation("items:null => halt")] + public static TItem MustNotBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] TCollection items, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (ReferenceEquals(parameter, other)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (items is null || parameter.IsOneOf(items)) { - Throw.SameObjectReference(parameter, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, items!); } return parameter; } /// - /// Ensures that and do not point to the same object instance, or otherwise - /// throws your custom exception. + /// Ensures that the specified URI has the "https" scheme, or otherwise throws an . /// - /// The first reference to be checked. - /// The second reference to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Thrown when both and point to the same object. + /// The URI to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when uses a different scheme than "https". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, T? other, Func exceptionFactory) - where T : class + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); + /// + /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. + /// + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "https", or when is a relative URI, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is greater than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (ReferenceEquals(parameter, other)) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) { - Throw.CustomException(exceptionFactory, parameter); + Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . - /// - /// The value to be checked. - /// The value that should not be approximately equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when the absolute difference between and is - /// less than 0.0001. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustNotBeApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustNotBeApproximately(other, 0.0001, parameterName, message); - /// - /// Ensures that the specified is not approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should not be approximately equal to. - /// - /// The delegate that creates your custom exception. and - /// are passed to this delegate. - /// - /// - /// Thrown when the absolute difference between and is - /// less than 0.0001. - /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is greater than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustNotBeApproximately(this double parameter, double other, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - if (parameter.IsApproximately(other)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) > 0) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter!, other); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . + /// Empty strings are regarded as trimmed. /// - /// The value to be checked. - /// The value that should not be approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. + /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when the absolute difference between and is - /// less than . + /// + /// Thrown when is not trimmed at the end, i.e. they end with white space characters. + /// Empty strings are regarded as trimmed. /// + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustNotBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.IsApproximately(other, tolerance)) + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) { - Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + Throw.NotTrimmedAtEnd(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the string is not null and trimmed at the end, or otherwise throws your custom exception. + /// Empty strings are regarded as trimmed. /// - /// The value to be checked. - /// The value that should not be approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. - /// The delegate that creates your custom exception. , - /// , and are passed to this delegate. - /// - /// Your custom exception thrown when the absolute difference between and - /// is less than . - /// + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not trimmed at the end. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustNotBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter.IsApproximately(other, tolerance)) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, using the default tolerance of 0.0001f, or otherwise throws an - /// . + /// Checks if the specified is true and throws an in this case. /// - /// The value to be checked. - /// The value that should not be approximately equal to. + /// The condition to be checked. The exception is thrown when it is true. + /// The message that will be passed to the . + /// Thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvalidState(bool condition, string? message = null) + { + if (condition) + { + Throw.InvalidState(message); + } + } + + /// + /// Ensures that can be cast to and returns the cast value, or otherwise throws a . + /// + /// The value to be cast. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when the absolute difference between and is - /// less than 0.0001f. + /// Thrown when cannot be cast to . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustNotBeApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustNotBeApproximately(other, 0.0001f, parameterName, message); + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message)is T castValue) + return castValue; + Throw.InvalidTypeCast(parameter, typeof(T), parameterName, message); + return default; + } + /// - /// Ensures that the specified is not approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that can be cast to and returns the cast value, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should not be approximately equal to. - /// - /// The delegate that creates your custom exception. and - /// are passed to this delegate. - /// - /// - /// Thrown when the absolute difference between and is - /// less than 0.0001. - /// + /// The value to be cast. + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when cannot be cast to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustNotBeApproximately(this float parameter, float other, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) { - if (parameter.IsApproximately(other)) - { - Throw.CustomException(exceptionFactory, parameter, other); - } - - return parameter; + if (parameter is T castValue) + return castValue; + Throw.CustomException(exceptionFactory, parameter); + return default; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . /// - /// The value to be checked. - /// The value that should not be approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. + /// The string to be checked. + /// The other string that must not contain . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when the absolute difference between and is - /// less than . - /// + /// Thrown when contains . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustNotBeApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.IsApproximately(other, tolerance)) + if (value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) { - Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); + Throw.Substring(parameter, value, parameterName, message); } return parameter; } /// - /// Ensures that the specified is not approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should not be approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// The delegate that creates your custom exception. , , and - /// are passed to this delegate. - /// + /// The string to be checked. + /// The other string that must not contain . + /// The delegate that creates your custom exception. and are passed to this delegate. /// - /// Your custom exception thrown when the absolute difference between and - /// is less than . + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustNotBeApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { - if (parameter.IsApproximately(other, tolerance)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.Contains(parameter)) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter, value!); } return parameter; } /// - /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . + /// Ensures that the string is not a substring of the specified other string, or otherwise throws a . /// - /// The nullable to be checked. + /// The string to be checked. + /// The other string that must not contain . + /// One of the enumeration values that specifies the rules for the search. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has no value. + /// Thrown when contains . + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : struct + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.HasValue) + value.MustNotBeNull(nameof(value), message); + parameter.MustNotBeNull(parameterName, message); + if (value.IndexOf(parameter, comparisonType) != -1) { - Throw.NullableHasNoValue(parameterName, message); + Throw.Substring(parameter, value, comparisonType, parameterName, message); } - return parameter.Value; + return parameter; } /// - /// Ensures that the specified nullable has a value and returns it, or otherwise throws your custom exception. + /// Ensures that the string is not a substring of the specified other string, or otherwise throws your custom exception. /// - /// The nullable to be checked. - /// The delegate that creates your custom exception. - /// Thrown when has no value. + /// The string to be checked. + /// The other string that must not contain . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) - where T : struct + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustNotBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { - if (!parameter.HasValue) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) != -1) { - Throw.CustomException(exceptionFactory); + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } - return parameter.Value; + return parameter; } /// @@ -1459,381 +1373,358 @@ public static ReadOnlySpan MustBeLongerThan(this ReadOnlySpan parameter } /// - /// Ensures that the specified object reference is not null, or otherwise throws an . + /// Ensures that the collection has the specified number of items, or otherwise throws an . /// - /// The object reference to be checked. + /// The collection to be checked. + /// The number of items the collection must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not have the specified number of items. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : class + public static TCollection MustHaveCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (parameter is null) + if (parameter!.Count(parameterName, message) != count) { - Throw.ArgumentNull(parameterName, message); + Throw.InvalidCollectionCount(parameter, count, parameterName, message); } return parameter; } /// - /// Ensures that the specified object reference is not null, or otherwise throws your custom exception. + /// Ensures that the collection has the specified number of items, or otherwise throws your custom exception. /// - /// The reference to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is null. + /// The collection to be checked. + /// The number of items the collection must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) - where T : class + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (parameter is null) + if (parameter is null || parameter.Count() != count) { - Throw.CustomException(exceptionFactory); + Throw.CustomException(exceptionFactory, parameter, count); } return parameter; } /// - /// Checks if the specified is true and throws an in this case. + /// Ensures that the collection does not contain the specified item, or otherwise throws an . /// - /// The condition to be checked. The exception is thrown when it is true. + /// The collection to be checked. + /// The item that must not be part of the collection. /// The name of the parameter (optional). - /// The message that will be passed to the (optional). - /// Thrown when is true. + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidArgument(bool condition, string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (condition) + if (parameter is ICollection collection) { - Throw.Argument(parameterName, message); + if (collection.Contains(item)) + { + Throw.ExistingItem(parameter, item, parameterName, message); + } + + return parameter; } - } - /// - /// Checks if the specified is true and throws your custom exception in this case. - /// - /// The condition to be checked. The exception is thrown when it is true. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static void InvalidArgument(bool condition, Func exceptionFactory) - { - if (condition) + if (parameter.MustNotBeNull(parameterName, message).Contains(item)) { - Throw.CustomException(exceptionFactory); + Throw.ExistingItem(parameter, item, parameterName, message); } + + return parameter; } /// - /// Checks if the specified is true and throws your custom exception in this case. + /// Ensures that the collection does not contain the specified item, or otherwise throws your custom exception. /// - /// The condition to be checked. The exception is thrown when it is true. - /// The value that is checked in the . This value is passed to the . - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when is true. + /// The collection to be checked. + /// The item that must not be part of the collection. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when contains . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static void InvalidArgument(bool condition, T parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (condition) + if (parameter is ICollection collection) { - Throw.CustomException(exceptionFactory, parameter); + if (collection.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + return parameter; + } + + if (parameter is null || parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); } + + return parameter; } /// - /// Checks if the specified character is a letter or digit. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLetterOrDigit(this char character) => char.IsLetterOrDigit(character); - /// - /// Checks if the given is equal to the specified or if it implements it. Internally, this - /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the interface type that implements. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrImplements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); - /// - /// Checks if the given is equal to the specified or if it implements it. This overload uses the specified - /// to compare the types. - /// - /// , - /// The type to be checked. - /// The type that is equivalent to or the interface type that implements. - /// The equality comparer used to compare the interface types. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrImplements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer); - /// - /// Checks if the specified string is trimmed, i.e. it does not start or end with - /// white space characters. Inputting an empty string will return true. When null is passed, - /// you can control the return value with which will - /// return true by default. - /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed, else false. An empty string will result in true. - /// You can control the return value with when the - /// is null. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmed(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmed(); - /// - /// Checks if the specified character span is trimmed, i.e. it does not start or end with - /// white space characters. Inputting an empty span will return true. - /// - /// The character span to be checked. - /// True if the is trimmed, else false. An empty span will result in true. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmed(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace() && !parameter[parameter.Length - 1].IsWhiteSpace(); - /// - /// Ensures that the string is not null and trimmed, or otherwise throws a . - /// Empty strings are regarded as trimmed. + /// Ensures that the string does not contain the specified value, or otherwise throws a . /// /// The string to be checked. + /// The string that must not be part of . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not trimmed, i.e. they start or end with white space characters. - /// Empty strings are regarded as trimmed. - /// - /// Thrown when is null. + /// Thrown when contains . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) + if (parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) { - Throw.NotTrimmed(parameter, parameterName, message); + Throw.StringContains(parameter, value, parameterName, message); } return parameter; } /// - /// Ensures that the string is not null and trimmed, or otherwise throws your custom exception. - /// Empty strings are regarded as trimmed. + /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. /// /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed. Empty strings are regarded as trimmed. + /// The string that must not be part of . + /// The delegate that creates your custom exception (optional). and are passed to this delegate. + /// + /// Your custom exception thrown when contains , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmed([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { - if (parameter is null || !parameter.AsSpan().IsTrimmed()) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || parameter.Contains(value)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, value!); } return parameter; } /// - /// Ensures that the string does not start with the specified value, or otherwise throws a . + /// Ensures that the string does not contain the specified value, or otherwise throws a . /// /// The string to be checked. - /// The other string that must not start with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The string that must not be part of . + /// One of the enumeration values that specifies the rules for the search. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when starts with . + /// Thrown when contains . /// Thrown when or is null. - public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + /// Thrown when is not a valid value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) + if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) >= 0) { - Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + Throw.StringContains(parameter, value, comparisonType, parameterName, message); } return parameter; } /// - /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// Ensures that the string does not contain the specified value, or otherwise throws your custom exception. /// /// The string to be checked. - /// The other string that must not start with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , + /// The string that must not be part of . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception (optional). , , and are passed to this delegate. + /// + /// Your custom exception thrown when contains , /// or when is null, /// or when is null. /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.StartsWith(value)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) >= 0) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } return parameter; } /// - /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. + /// Ensures that the does not contain the specified item, or otherwise throws an . /// - /// The string to be checked. - /// The other string that must not start with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// - /// Thrown when is not a valid value. + /// The to be checked. + /// The item that must not be part of the . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when contains . + /// + /// The default instance of cannot contain any items, so this method will not throw for default instances. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + public static ImmutableArray MustNotContain(this ImmutableArray parameter, T item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.StartsWith(value, comparisonType)) + if (!parameter.IsDefault && parameter.Contains(item)) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.ExistingItem(parameter, item, parameterName, message); } return parameter; } /// - /// Ensures that the span does not start with the specified value, or otherwise throws a . + /// Ensures that the does not contain the specified item, or otherwise throws your custom exception. /// - /// The span to be checked. - /// The other span that must not start with. - /// One of the enumeration values that specifies the rules for the search. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when starts with . - /// Thrown when or is null. + /// The to be checked. + /// The item that must not be part of the . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when contains . + /// + /// The default instance of cannot contain any items, so this method will not throw for default instances. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("exceptionFactory:null => halt")] + public static ImmutableArray MustNotContain(this ImmutableArray parameter, T item, Func, T, Exception> exceptionFactory) { - if (parameter.StartsWith(value, comparisonType)) + if (!parameter.IsDefault && parameter.Contains(item)) { - Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, item); } return parameter; } /// - /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. + /// Ensures that the URI has one of the specified schemes, or otherwise throws an . /// - /// The span to be checked. - /// The other span that must not start with. - /// - /// The delegate that creates your custom exception. and - /// are passed to this delegate. - /// - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// + /// The URI to be checked. + /// One of these strings must be equal to the scheme of the URI. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the scheme is not equal to one of the specified schemes. + /// Thrown when is relative and thus has no scheme. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, ReadOnlySpansExceptionFactory exceptionFactory) - where T : IEquatable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; schemes:null => halt")] + public static Uri MustHaveOneSchemeOf([NotNull][ValidatedNotNull] this Uri? parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.StartsWith(value)) + // ReSharper disable PossibleMultipleEnumeration + parameter.MustBeAbsoluteUri(parameterName, message); + if (schemes is ICollection collection) { - Throw.CustomSpanException(exceptionFactory, parameter, value); + if (!collection.Contains(parameter.Scheme)) + { + Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); + } + + return parameter; + } + + if (!schemes.MustNotBeNull(nameof(schemes), message).Contains(parameter.Scheme)) + { + Throw.UriMustHaveOneSchemeOf(parameter, schemes, parameterName, message); } return parameter; + // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. + /// Ensures that the URI has one of the specified schemes, or otherwise throws your custom exception. /// - /// The span to be checked. - /// The other span that must not start with. - /// One of the enumeration values that specifies the rules for the search. - /// - /// The delegate that creates your custom exception. , - /// , and are passed to this delegate. - /// - /// - /// Your custom exception thrown when does not start with , - /// or when is null, - /// or when is null. - /// - /// - /// Thrown when is not a valid value. - /// + /// The URI to be checked. + /// One of these strings must be equal to the scheme of the URI. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the scheme is not equal to one of the specified schemes, or when is a relative URI, or when is null. + /// Thrown when is null. + /// The type of the collection containing the schemes. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, StringComparison comparisonType, ReadOnlySpansExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustHaveOneSchemeOf([NotNull][ValidatedNotNull] this Uri? parameter, TCollection schemes, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (parameter.StartsWith(value, comparisonType)) + if (parameter is null || !parameter.IsAbsoluteUri) { - Throw.CustomSpanException(exceptionFactory, parameter, value, comparisonType); + Throw.CustomException(exceptionFactory, parameter, schemes); + } + + if (schemes is ICollection collection) + { + if (!collection.Contains(parameter.Scheme)) + { + Throw.CustomException(exceptionFactory, parameter, schemes); + } + + return parameter; + } + + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (schemes is null || !schemes.Contains(parameter.Scheme)) + { + Throw.CustomException(exceptionFactory, parameter, schemes!); } return parameter; } /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws a . + /// Ensures that the string does not end with the specified value, or otherwise throws a . /// /// The string to be checked. - /// The other string that must contain . + /// The other string must not end with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . + /// Thrown when ends with . /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// Thrown when is not a valid value. + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) + if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) { - Throw.NotSubstring(parameter, value, parameterName, message); + Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); } return parameter; } /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the string does not end with the specified value, or otherwise throws your custom exception. /// /// The string to be checked. - /// The other string that must contain . + /// The other string must not end with. /// The delegate that creates your custom exception. and are passed to this delegate. /// - /// Your custom exception thrown when does not contain , + /// Your custom exception thrown when ends with , /// or when is null, /// or when is null. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !value.Contains(parameter)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value)) { Throw.CustomException(exceptionFactory, parameter, value!); } @@ -1842,49 +1733,23 @@ public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? } /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws a . - /// - /// The string to be checked. - /// The other string that must contain . - /// One of the enumeration values that specifies the rules for the search. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - value.MustNotBeNull(nameof(value), message); - parameter.MustNotBeNull(parameterName, message); - if (value.IndexOf(parameter, comparisonType) == -1) - { - Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); - } - - return parameter; - } - - /// - /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. + /// Ensures that the string does not end with the specified value, or otherwise throws your custom exception. /// /// The string to be checked. - /// The other string that must contain . + /// The other string must not end with. /// One of the enumeration values that specifies the rules for the search. /// The delegate that creates your custom exception. , , and are passed to this delegate. /// - /// Your custom exception thrown when does not contain , + /// Your custom exception thrown when ends with , /// or when is null, /// or when is null. /// - /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] - public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.EndsWith(value, comparisonType)) { Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } @@ -1893,374 +1758,437 @@ public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? } /// - /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws an . + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The URI to be checked. + /// The value to be checked. + /// The value that should be less than or approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "http" or "https". - /// Thrown when is relative and thus has no scheme. - /// Thrown when is null. + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (parameter.MustBeAbsoluteUri(parameterName, message).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) - { - Throw.UriMustHaveOneSchemeOf(parameter, ["https", "http"], parameterName, message); - } - - return parameter; - } - + public static double MustBeLessThanOrApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeLessThanOrApproximately(other, 0.0001, parameterName, message); /// - /// Ensures that the specified URI has the "http" or "https" scheme, or otherwise throws your custom exception. + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// Your custom exception thrown when uses a different scheme than "http" or "https", or when is a relative URI, or when is null. + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpOrHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static double MustBeLessThanOrApproximately(this double parameter, double other, Func exceptionFactory) { - if (parameter.MustBeAbsoluteUri(exceptionFactory).Scheme.Equals("https") == false && parameter.Scheme.Equals("http") == false) + if (!parameter.IsLessThanOrApproximately(other)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws a . + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws an . /// - /// The string to be checked. - /// The length that the string must be shorter than or equal to. + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length greater than . - /// Thrown when is null. + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Length > length) + if (!parameter.IsLessThanOrApproximately(other, tolerance)) { - Throw.StringNotShorterThanOrEqualTo(parameter, length, parameterName, message); + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the string is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The length that the string must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length greater than . + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - if (parameter is null || parameter.Length > length) + if (!parameter.IsLessThanOrApproximately(other, tolerance)) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } return parameter; } /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. + /// The value to be checked. + /// The value that should be less than or approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than . + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - ((ReadOnlySpan)parameter).MustBeShorterThanOrEqualTo(length, parameterName, message); - return parameter; - } - + public static float MustBeLessThanOrApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeLessThanOrApproximately(other, 0.0001f, parameterName, message); /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// Ensures that the specified is less than or approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than . + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + public static float MustBeLessThanOrApproximately(this float parameter, float other, Func exceptionFactory) { - if (parameter.Length > length) + if (!parameter.IsLessThanOrApproximately(other)) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws an . + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws an . /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than . + /// + /// Thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Length > length) + if (!parameter.IsLessThanOrApproximately(other, tolerance)) { - Throw.SpanMustBeShorterThanOrEqualTo(parameter, length, parameterName, message); + Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the span is shorter than or equal to the specified length, or otherwise throws your custom exception. + /// Ensures that the specified is less than or approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The span to be checked. - /// The length value that the span must be shorter than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than . + /// The value to be checked. + /// The value that should be less than or approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when is not less than or approximately equal to . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - if (parameter.Length > length) + if (!parameter.IsLessThanOrApproximately(other, tolerance)) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } return parameter; } /// - /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws an , an , or a . + /// Checks if the specified character is a white space character. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsWhiteSpace(this char character) => char.IsWhiteSpace(character); + /// + /// Checks if the specified string is trimmed, i.e. it does not start or end with + /// white space characters. Inputting an empty string will return true. When null is passed, + /// you can control the return value with which will + /// return true by default. /// /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed, else false. An empty string will result in true. + /// You can control the return value with when the + /// is null. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmed(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmed(); + /// + /// Checks if the specified character span is trimmed, i.e. it does not start or end with + /// white space characters. Inputting an empty span will return true. + /// + /// The character span to be checked. + /// True if the is trimmed, else false. An empty span will result in true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmed(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace() && !parameter[parameter.Length - 1].IsWhiteSpace(); + /// + /// Ensures that the collection contains the specified item, or otherwise throws a . + /// + /// The collection to be checked. + /// The item that must be part of the collection. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when contains only white space. - /// Thrown when is an empty string. + /// Thrown when does not contain . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustNotBeNullOrWhiteSpace([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static TCollection MustContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - parameter.MustNotBeNullOrEmpty(parameterName, message); - foreach (var character in parameter) + if (parameter is ICollection collection) { - if (!character.IsWhiteSpace()) + if (!collection.Contains(item)) { - return parameter; + Throw.MissingItem(parameter, item, parameterName, message); } + + return parameter; } - Throw.WhiteSpaceString(parameter, parameterName, message); - return null; + if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) + { + Throw.MissingItem(parameter, item, parameterName, message); + } + + return parameter; } /// - /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws your custom exception. + /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null, empty, or contains only white space. + /// The collection to be checked. + /// The item that must be part of the collection. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory: null => halt")] - public static string MustNotBeNullOrWhiteSpace([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (parameter.IsNullOrWhiteSpace()) + if (parameter is ICollection collection) { - Throw.CustomException(exceptionFactory, parameter); + if (!collection.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); + } + + return parameter; + } + + if (parameter is null || !parameter.Contains(item)) + { + Throw.CustomException(exceptionFactory, parameter, item); } return parameter; } /// - /// Checks if the two specified types are equivalent. This is true when both types are equal or - /// when one type is a constructed generic type and the other type is the corresponding generic type definition. + /// Ensures that the string contains the specified substring, or otherwise throws a . /// - /// The first type to be checked. - /// The other type to be checked. - /// - /// True if both types are null, or if both are equal, or if one type - /// is a constructed generic type and the other one is the corresponding generic type definition, else false. - /// + /// The string to be checked. + /// The substring that must be part of . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEquivalentTypeTo(this Type? type, Type? other) => ReferenceEquals(type, other) || (type is not null && other is not null && (type == other || (type.IsConstructedGenericType != other.IsConstructedGenericType && CheckTypeEquivalency(type, other)))); - private static bool CheckTypeEquivalency(Type type, Type other) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (type.IsConstructedGenericType) + if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) { - return type.GetGenericTypeDefinition() == other; + Throw.StringDoesNotContain(parameter, value, parameterName, message); } - return other.GetGenericTypeDefinition() == type; + return parameter; } /// - /// Checks if the value is within the specified range. + /// Ensures that the string contains the specified value, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The range where must be in-between. - /// True if the parameter is within the specified range, else false. - /// Thrown when is null. + /// The string to be checked. + /// The substring that must be part of . + /// The delegate that creates you custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsIn([NotNull][ValidatedNotNull] this T parameter, Range range) - where T : IComparable => range.IsValueWithinRange(parameter); + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || !parameter.Contains(value)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; + } + /// - /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// Ensures that the string contains the specified value, or otherwise throws a . /// - /// The comparable to be checked. - /// The boundary value that must be less than . + /// The string to be checked. + /// The substring that must be part of . + /// One of the enumeration values that specifies the rules for the search. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than or equal to . - /// Thrown when is null. + /// Thrown when does not contain . + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) + if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) { - Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); + Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); } return parameter; } /// - /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// Ensures that the string contains the specified value, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than or equal to , or when is null. + /// The string to be checked. + /// The substring that must be part of . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates you custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) + if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) { - Throw.CustomException(exceptionFactory, parameter!, other); + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } return parameter; } /// - /// Checks if the given type derives from the specified base class or interface type. Internally, this method uses - /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type describing an interface or base class that should derive from or implement. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] - public static bool InheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClassOrInterfaceType) => baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType) : type.DerivesFrom(baseClassOrInterfaceType); - /// - /// Checks if the given type derives from the specified base class or interface type. This overload uses the specified - /// to compare the types. - /// - /// The type to be checked. - /// The type describing an interface or base class that should derive from or implement. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] - public static bool InheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClassOrInterfaceType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType, typeComparer) : type.DerivesFrom(baseClassOrInterfaceType, typeComparer); - /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the immutable array contains the specified item, or otherwise throws a . /// - /// The date time to be checked. + /// The immutable array to be checked. + /// The item that must be part of the immutable array. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// Thrown when does not contain . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ImmutableArray MustContain(this ImmutableArray parameter, T item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Kind != DateTimeKind.Unspecified) + if (!parameter.Contains(item)) { - Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); + Throw.MissingItem(parameter, item, parameterName, message); } return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the immutable array contains the specified item, or otherwise throws your custom exception. /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . + /// The immutable array to be checked. + /// The item that must be part of the immutable array. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) + public static ImmutableArray MustContain(this ImmutableArray parameter, T item, Func, T, Exception> exceptionFactory) { - if (parameter.Kind != DateTimeKind.Unspecified) + if (!parameter.Contains(item)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, item); } return parameter; } /// - /// Checks if the specified string is null, empty, or contains only white space. - /// - /// The string to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] - public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? @string) => string.IsNullOrWhiteSpace(@string); - /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that and do not point to the same object instance, or otherwise + /// throws a . /// - /// The date time to be checked. + /// The first reference to be checked. + /// The second reference to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// Thrown when both and point to the same object. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeLocal(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : class { - if (parameter.Kind != DateTimeKind.Local) + if (ReferenceEquals(parameter, other)) { - Throw.MustBeLocalDateTime(parameter, parameterName, message); + Throw.SameObjectReference(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that and do not point to the same object instance, or otherwise + /// throws your custom exception. /// - /// The date time to be checked. + /// The first reference to be checked. + /// The second reference to be checked. /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . + /// Thrown when both and point to the same object. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeLocal(this DateTime parameter, Func exceptionFactory) + public static T? MustNotBeSameAs([NoEnumeration] this T? parameter, T? other, Func exceptionFactory) + where T : class { - if (parameter.Kind != DateTimeKind.Local) + if (ReferenceEquals(parameter, other)) { Throw.CustomException(exceptionFactory, parameter); } @@ -2269,123 +2197,182 @@ public static DateTime MustBeLocal(this DateTime parameter, Func - /// Ensures that the value is one of the specified items, or otherwise throws a . + /// Checks if the specified is true and throws an in this case. /// - /// The value to be checked. - /// The items that should contain the value. + /// The condition to be checked. The exception is thrown when it is true. + /// The message that will be passed to the (optional). + /// Thrown when is true. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void InvalidOperation(bool condition, string? message = null) + { + if (condition) + { + Throw.InvalidOperation(message); + } + } + + /// + /// Ensures that the string's length is within the specified range, or otherwise throws a . + /// + /// The string to be checked. + /// The range where the string's length must be in-between. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to one of the specified . - /// Thrown when is null. + /// Thrown when the length of is not with the specified . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - [NotNull][ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable PossibleMultipleEnumeration - if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) { - Throw.ValueNotOneOf(parameter, items, parameterName, message); + Throw.StringLengthNotInRange(parameter, range, parameterName, message); } return parameter; - // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the value is one of the specified items, or otherwise throws your custom exception. + /// Ensures that the string's length is within the specified range, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The items that should contain the value. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to one of the specified , or when is null. + /// The string to be checked. + /// The range where the string's length must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or its length is not within the specified range. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] TCollection items, Func exceptionFactory) - where TCollection : class, IEnumerable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (items is null || !parameter.IsOneOf(items)) + if (parameter is null || !range.IsValueWithinRange(parameter.Length)) { - Throw.CustomException(exceptionFactory, parameter, items!); + Throw.CustomException(exceptionFactory, parameter, range); } return parameter; } /// - /// Ensures that is equal to using the default equality comparer, or otherwise throws a . + /// Ensures that the 's length is within the specified range, or otherwise throws an . /// - /// The first value to be compared. - /// The other value to be compared. + /// The to be checked. + /// The range where the 's length must be in-between. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are not equal. + /// Thrown when the length of is not within the specified . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ImmutableArray MustHaveLengthIn(this ImmutableArray parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!EqualityComparer.Default.Equals(parameter, other)) + var length = parameter.IsDefault ? 0 : parameter.Length; + if (!range.IsValueWithinRange(length)) { - Throw.ValuesNotEqual(parameter, other, parameterName, message); + Throw.ImmutableArrayLengthNotInRange(parameter, range, parameterName, message); } return parameter; } /// - /// Ensures that is equal to using the default equality comparer, or otherwise throws your custom exception. + /// Ensures that the 's length is within the specified range, or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The other value to be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when and are not equal. + /// The to be checked. + /// The range where the 's length must be in-between. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the length of is not within the specified range. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBe(this T parameter, T other, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static ImmutableArray MustHaveLengthIn(this ImmutableArray parameter, Range range, Func, Range, Exception> exceptionFactory) { - if (!EqualityComparer.Default.Equals(parameter, other)) + var length = parameter.IsDefault ? 0 : parameter.Length; + if (!range.IsValueWithinRange(length)) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, range); } return parameter; } /// - /// Ensures that is equal to using the specified equality comparer, or otherwise throws a . + /// Checks if and point to the same object. /// - /// The first value to be compared. - /// The other value to be compared. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + // ReSharper disable StringLiteralTypo + [ContractAnnotation("parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull")] + // ReSharper restore StringLiteralTypo + public static bool IsSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other) + where T : class => ReferenceEquals(parameter, other); + /// + /// Ensures that is not equal to using the default equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when and are equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (EqualityComparer.Default.Equals(parameter, other)) + { + Throw.ValuesEqual(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that is not equal to using the default equality comparer, or otherwise throws your custom exception. + /// + /// The first value to be compared. + /// The other value to be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when and are equal. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustNotBe(this T parameter, T other, Func exceptionFactory) + { + if (EqualityComparer.Default.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + + return parameter; + } + + /// + /// Ensures that is not equal to using the specified equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. /// The equality comparer used for comparing the two values. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are not equal. + /// Thrown when and are equal. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("equalityComparer:null => halt")] - public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) + if (equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) { - Throw.ValuesNotEqual(parameter, other, parameterName, message); + Throw.ValuesEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that is equal to using the specified equality comparer, or otherwise throws your custom exception. + /// Ensures that is not equal to using the specified equality comparer, or otherwise throws your custom exception. /// /// The first value to be compared. /// The other value to be compared. /// The equality comparer used for comparing the two values. /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when and are not equal, or when is null. + /// Your custom exception thrown when and are equal, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("equalityComparer:null => halt")] - public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) + public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (equalityComparer is null || !equalityComparer.Equals(parameter, other)) + if (equalityComparer is null || equalityComparer.Equals(parameter, other)) { Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); } @@ -2394,79 +2381,80 @@ public static T MustBe(this T parameter, T other, IEqualityComparer equali } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// Ensures that the two strings are not equal using the specified , or otherwise throws a . /// /// The first string to be compared. /// The second string to be compared. /// The enum value specifying how the two strings should be compared. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to . + /// Thrown when is equal to . /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!string.Equals(parameter, other, comparisonType)) + if (string.Equals(parameter, other, comparisonType)) { - Throw.ValuesNotEqual(parameter, other, parameterName, message); + Throw.ValuesEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. /// /// The first string to be compared. /// The second string to be compared. /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to . + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when is equal to . /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) + public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) { - if (!string.Equals(parameter, other, comparisonType)) + if (string.Equals(parameter, other, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws a . + /// Ensures that the two strings are not equal using the specified , or otherwise throws a . /// /// The first string to be compared. /// The second string to be compared. /// The enum value specifying how the two strings should be compared. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to . + /// Thrown when is equal to . /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.Equals(other, comparisonType)) + if (parameter.Equals(other, comparisonType)) { - Throw.ValuesNotEqual(parameter, other, parameterName, message); + Throw.ValuesEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. + /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. /// /// The first string to be compared. /// The second string to be compared. /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not equal to . + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when is equal to . /// Thrown when is not a valid value from the enum. - public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) { - if (!parameter.Equals(other, comparisonType)) + if (parameter.Equals(other, comparisonType)) { Throw.CustomException(exceptionFactory, parameter, other, comparisonType); } @@ -2475,267 +2463,284 @@ public static T MustBe(this T parameter, T other, IEqualityComparer equali } /// - /// Checks if the specified is true and throws an in this case. + /// Ensures that the specified is less than the given value, or otherwise throws an . /// - /// The condition to be checked. The exception is thrown when it is true. - /// The message that will be passed to the (optional). - /// Thrown when is true. + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is not less than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InvalidOperation(bool condition, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (condition) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) { - Throw.InvalidOperation(message); + Throw.MustBeLessThan(parameter, other, parameterName, message); + } + + return parameter; + } + + /// + /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is not less than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) >= 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); } + + return parameter; } /// - /// Ensures that the string matches the specified regular expression, or otherwise throws a . + /// Ensures that the string is not null and trimmed, or otherwise throws a . + /// Empty strings are regarded as trimmed. /// /// The string to be checked. - /// The regular expression used for pattern matching. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not match the specified regular expression. - /// Thrown when or is null. + /// + /// Thrown when is not trimmed, i.e. they start or end with white space characters. + /// Empty strings are regarded as trimmed. + /// + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; regex:null => halt")] - public static string MustMatch([NotNull][ValidatedNotNull] this string? parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmed([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!regex.MustNotBeNull(nameof(regex), message).IsMatch(parameter.MustNotBeNull(parameterName, message))) + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmed()) { - Throw.StringDoesNotMatch(parameter, regex, parameterName, message); + Throw.NotTrimmed(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string matches the specified regular expression, or otherwise throws your custom exception. + /// Ensures that the string is not null and trimmed, or otherwise throws your custom exception. + /// Empty strings are regarded as trimmed. /// /// The string to be checked. - /// The regular expression used for pattern matching. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when does not match the specified regular expression, - /// or when is null, - /// or when is null. - /// + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not trimmed. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string MustMatch([NotNull][ValidatedNotNull] this string? parameter, Regex regex, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmed([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || regex is null || !regex.IsMatch(parameter)) + if (parameter is null || !parameter.AsSpan().IsTrimmed()) { - Throw.CustomException(exceptionFactory, parameter, regex!); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that can be cast to and returns the cast value, or otherwise throws a . + /// Ensures that the string is a substring of the specified other string, or otherwise throws a . /// - /// The value to be cast. + /// The string to be checked. + /// The other string that must contain . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when cannot be cast to . - /// Thrown when is null. + /// Thrown when does not contain . + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message)is T castValue) - return castValue; - Throw.InvalidTypeCast(parameter, typeof(T), parameterName, message); - return default; + if (!value.MustNotBeNull(nameof(value), message).Contains(parameter.MustNotBeNull(parameterName, message))) + { + Throw.NotSubstring(parameter, value, parameterName, message); + } + + return parameter; } /// - /// Ensures that can be cast to and returns the cast value, or otherwise throws your custom exception. + /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. /// - /// The value to be cast. - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when cannot be cast to . + /// The string to be checked. + /// The other string that must contain . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeOfType([NotNull, ValidatedNotNull, NoEnumeration] this object? parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) { - if (parameter is T castValue) - return castValue; - Throw.CustomException(exceptionFactory, parameter); - return default; + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || !value.Contains(parameter)) + { + Throw.CustomException(exceptionFactory, parameter, value!); + } + + return parameter; } /// - /// Checks if the specified value is a valid enum value of its type. This is true when the specified value - /// is one of the constants defined in the enum, or a valid flags combination when the enum type is marked - /// with the . + /// Ensures that the string is a substring of the specified other string, or otherwise throws a . /// - /// The type of the enum. - /// The enum value to be checked. + /// The string to be checked. + /// The other string that must contain . + /// One of the enumeration values that specifies the rules for the search. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain . + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsValidEnumValue(this T parameter) - where T : struct, Enum => EnumInfo.IsValidEnumValue(parameter); - /// - /// Checks if and point to the same object. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - // ReSharper disable StringLiteralTypo - [ContractAnnotation("parameter:notNull => true, other:notnull; parameter:notNull => false, other:canbenull; other:notnull => true, parameter:notnull; other:notnull => false, parameter:canbenull")] - // ReSharper restore StringLiteralTypo - public static bool IsSameAs([NoEnumeration] this T? parameter, [NoEnumeration] T? other) - where T : class => ReferenceEquals(parameter, other); - /// - /// Checks if the specified value is approximately the same as the other value, using the given tolerance. - /// - /// The first value to be compared. - /// The second value to be compared. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if and are equal or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this double value, double other, double tolerance) => Math.Abs(value - other) <= tolerance; - /// - /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001. - /// - /// The first value to be compared. - /// The second value to be compared. - /// - /// True if and are equal or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this double value, double other) => Math.Abs(value - other) <= 0.0001; + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + value.MustNotBeNull(nameof(value), message); + parameter.MustNotBeNull(parameterName, message); + if (value.IndexOf(parameter, comparisonType) == -1) + { + Throw.NotSubstring(parameter, value, comparisonType, parameterName, message); + } + + return parameter; + } + /// - /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// Ensures that the string is a substring of the specified other string, or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The second value to be compared. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if and are equal or if their absolute difference - /// is smaller than the given , otherwise false. - /// + /// The string to be checked. + /// The other string that must contain . + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// + /// Your custom exception thrown when does not contain , + /// or when is null, + /// or when is null. + /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this float value, float other, float tolerance) => Math.Abs(value - other) <= tolerance; + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt")] + public static string MustBeSubstringOf([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || value is null || value.IndexOf(parameter, comparisonType) == -1) + { + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + } + + return parameter; + } + /// - /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001f. + /// Checks if the string is either "\n" or "\r\n". This is done independently of the current value of . /// - /// The first value to be compared. - /// The second value to be compared. - /// - /// True if and are equal or if their absolute difference - /// is smaller than 0.0001f, otherwise false. - /// + /// The string to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsApproximately(this float value, float other) => Math.Abs(value - other) <= 0.0001f; + [ContractAnnotation("=> false, parameter:canbenull; => true, parameter:notnull")] + public static bool IsNewLine([NotNullWhen(true)] this string? parameter) => parameter == "\n" || parameter == "\r\n"; /// - /// Ensures that is within the specified range, or otherwise throws an . + /// Ensures that the specified is greater than the given value, or otherwise throws an . /// - /// The type of the parameter to be checked. - /// The parameter to be checked. - /// The range where must be in-between. + /// The comparable to be checked. + /// The boundary value that must be less than . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not within . + /// Thrown when the specified is less than or equal to . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustBeGreaterThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : IComparable { - if (!range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) { - Throw.MustBeInRange(parameter, range, parameterName, message); + Throw.MustBeGreaterThan(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that is within the specified range, or otherwise throws your custom exception. + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. /// - /// The parameter to be checked. - /// The range where must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is not within , or when is null. + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than or equal to , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) + public static T MustBeGreaterThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) where T : IComparable { // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || !range.IsValueWithinRange(parameter)) + if (parameter is null || parameter.CompareTo(other) <= 0) { - Throw.CustomException(exceptionFactory, parameter!, range); + Throw.CustomException(exceptionFactory, parameter!, other); } return parameter; } /// - /// Checks if the specified string is trimmed at the start, i.e. it does not start with - /// white space characters. Inputting an empty string will return true. - /// - /// The string to be checked. - /// - /// The value indicating whether true or false should be returned from this method when the - /// is null. The default value is true. - /// - /// - /// True if the is trimmed at the start, else false. - /// An empty string will result in true. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtStart(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtStart(); - /// - /// Checks if the specified character span is trimmed at the start, i.e. it does not start with - /// white space characters. Inputting an empty span will return true. + /// Checks if the specified value is a valid enum value of its type. This is true when the specified value + /// is one of the constants defined in the enum, or a valid flags combination when the enum type is marked + /// with the . /// - /// The character span to be checked. - /// - /// True if the is trimmed at the start, else false. - /// An empty span will result in true. - /// + /// The type of the enum. + /// The enum value to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsTrimmedAtStart(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace(); + public static bool IsValidEnumValue(this T parameter) + where T : struct, Enum => EnumInfo.IsValidEnumValue(parameter); /// - /// Ensures that the has the specified scheme, or otherwise throws an . + /// Ensures that the specified enum value is valid, or otherwise throws an . An enum value + /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type + /// is marked with the . /// - /// The URI to be checked. - /// The scheme that the URI should have. + /// The type of the enum. + /// The value to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than the specified one. - /// Thrown when is relative and thus has no scheme. - /// Thrown when is null. + /// Thrown when is no valid enum value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : struct, Enum { - if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) + if (!EnumInfo.IsValidEnumValue(parameter)) { - Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); + Throw.EnumValueNotDefined(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// Ensures that the specified enum value is valid, or otherwise throws your custom exception. An enum value + /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type + /// is marked with the . /// - /// The URI to be checked. - /// The scheme that the URI should have. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// Your custom exception thrown when uses a different scheme than the specified one, or when is a relative URI, or when is null. + /// The type of the enum. + /// The value to be checked. + /// The delegate that creates your custom exception. The is passed to this delegate. + /// Your custom exception thrown when is no valid enum value, or when is no enum type. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustBeValidEnumValue(this T parameter, Func exceptionFactory) + where T : struct, Enum { - if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) + if (!EnumInfo.IsValidEnumValue(parameter)) { Throw.CustomException(exceptionFactory, parameter); } @@ -2744,99 +2749,79 @@ public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter } /// - /// Ensures that the has the specified scheme, or otherwise throws your custom exception. + /// Checks if the specified string is null, empty, or contains only white space. /// - /// The URI to be checked. - /// The scheme that the URI should have. - /// The delegate that creates the exception to be thrown. and are passed to this delegate. - /// Your custom exception thrown when uses a different scheme than the specified one, or when is a relative URI, or when is null. + /// The string to be checked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) + [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] + public static bool IsNullOrWhiteSpace([NotNullWhen(false)] this string? @string) => string.IsNullOrWhiteSpace(@string); + /// + /// Ensures that the specified GUID is not empty, or otherwise throws an . + /// + /// The GUID to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is an empty GUID. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Guid MustNotBeEmpty(this Guid parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) + if (parameter == Guid.Empty) { - Throw.CustomException(exceptionFactory, parameter, scheme); + Throw.EmptyGuid(parameterName, message); } return parameter; } /// - /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. + /// Ensures that the specified GUID is not empty, or otherwise throws your custom exception. /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is less than or if their absolute difference - /// is smaller than the given , otherwise false. - /// + /// The GUID to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is an empty GUID. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this double value, double other, double tolerance) => value < other || value.IsApproximately(other, tolerance); + [ContractAnnotation("exceptionFactory:null => halt")] + public static Guid MustNotBeEmpty(this Guid parameter, Func exceptionFactory) + { + if (parameter == Guid.Empty) + { + Throw.CustomException(exceptionFactory); + } + + return parameter; + } + /// - /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001. + /// Ensures that the string is either "\n" or "\r\n", or otherwise throws a . This is done independently of the current value of . /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is less than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// + /// The string to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is not equal to "\n" or "\r\n". + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this double value, double other) => value < other || value.IsApproximately(other); - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is less than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this float value, float other, float tolerance) => value < other || value.IsApproximately(other, tolerance); - /// - /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001f. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is less than or if their absolute difference - /// is smaller than 0.0001f, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLessThanOrApproximately(this float value, float other) => value < other || value.IsApproximately(other); - /// - /// Ensures that the specified is not default or empty, or otherwise throws an . - /// - /// The to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is default or empty. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.IsDefaultOrEmpty) + if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) { - Throw.EmptyCollection(parameterName, message); + Throw.NotNewLine(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified is not default or empty, or otherwise throws your custom exception. + /// Ensures that the string is either "\n" or "\r\n", or otherwise throws your custom exception. This is done independently of the current value of . /// - /// The to be checked. - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when is default or empty. + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is not equal to "\n" or "\r\n". [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray parameter, Func, Exception> exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter.IsDefaultOrEmpty) + if (!parameter.IsNewLine()) { Throw.CustomException(exceptionFactory, parameter); } @@ -2844,27 +2829,6 @@ public static ImmutableArray MustNotBeDefaultOrEmpty(this ImmutableArray - /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . - /// - /// The URI to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "http". - /// Thrown when is relative and thus has no scheme. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); - /// - /// Ensures that the specified URI has the "http" scheme, or otherwise throws your custom exception. - /// - /// The URI to be checked. - /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// Your custom exception thrown when uses a different scheme than "http", or when is a relative URI, or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); /// /// Ensures that the string starts with the specified value, or otherwise throws a . /// @@ -2939,857 +2903,974 @@ public static string MustStartWith([NotNull, ValidatedNotNull] this string? para } /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is greater than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this double value, double other, double tolerance) => value > other || value.IsApproximately(other, tolerance); - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is greater than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this double value, double other) => value > other || value.IsApproximately(other); - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the given tolerance. - /// - /// The first value to compare. - /// The second value to compare. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// True if is greater than or if their absolute difference - /// is smaller than the given , otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this float value, float other, float tolerance) => value > other || value.IsApproximately(other, tolerance); - /// - /// Checks if the specified value is greater than or approximately the same as the other value, using the default tolerance of 0.0001f. - /// - /// The first value to compare. - /// The second value to compare. - /// - /// True if is greater than or if their absolute difference - /// is smaller than 0.0001, otherwise false. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsGreaterThanOrApproximately(this float value, float other) => value > other || value.IsApproximately(other); - /// - /// Ensures that is not within the specified range, or otherwise throws an . + /// Ensures that the string is not null and trimmed at the start, or otherwise throws a . + /// Empty strings are regarded as trimmed. /// - /// The type of the parameter to be checked. - /// The parameter to be checked. - /// The range where must not be in-between. + /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is within . + /// + /// Thrown when is not trimmed at the start, i.e. they start with white space characters. + /// Empty strings are regarded as trimmed. + /// /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static string MustBeTrimmedAtStart([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (range.IsValueWithinRange(parameter.MustNotBeNullReference(parameterName, message))) + if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) { - Throw.MustNotBeInRange(parameter, range, parameterName, message); + Throw.NotTrimmedAtStart(parameter, parameterName, message); } return parameter; } /// - /// Ensures that is not within the specified range, or otherwise throws your custom exception. + /// Ensures that the string is not null and trimmed at the start, or otherwise throws your custom exception. + /// Empty strings are regarded as trimmed. /// - /// The parameter to be checked. - /// The range where must not be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is within , or when is null. + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or not trimmed at the start. Empty strings are regarded as trimmed. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeIn([NotNull][ValidatedNotNull] this T parameter, Range range, Func, Exception> exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeTrimmedAtStart([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || range.IsValueWithinRange(parameter)) + if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) { - Throw.CustomException(exceptionFactory, parameter!, range); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the string is longer than or equal to the specified length, or otherwise throws a . + /// Ensures that the value is one of the specified items, or otherwise throws a . /// - /// The string to be checked. - /// The length that the string must be longer than or equal to. + /// The value to be checked. + /// The items that should contain the value. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length shorter than . - /// Thrown when is null. + /// Thrown when is not equal to one of the specified . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("items:null => halt")] + public static TItem MustBeOneOf(this TItem parameter, // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + [NotNull][ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Length < length) + // ReSharper disable PossibleMultipleEnumeration + if (!parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) { - Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); + Throw.ValueNotOneOf(parameter, items, parameterName, message); } return parameter; + // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the string is longer than or equal to the specified length, or otherwise throws your custom exception. + /// Ensures that the value is one of the specified items, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The length that the string must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length shorter than . + /// The value to be checked. + /// The items that should contain the value. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to one of the specified , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeLongerThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + [ContractAnnotation("items:null => halt")] + public static TItem MustBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] TCollection items, Func exceptionFactory) + where TCollection : class, IEnumerable { - if (parameter is null || parameter.Length < length) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (items is null || !parameter.IsOneOf(items)) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter, items!); } return parameter; } /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// Checks if the given is equal to the specified or if it derives from it. Internally, this + /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than . + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - ((ReadOnlySpan)parameter).MustBeLongerThanOrEqualTo(length, parameterName, message); - return parameter; - } - + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrDerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. + /// Checks if the given is equal to the specified or if it derives from it. This overload uses the specified + /// to compare the types. /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than . + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) - { - if (parameter.Length < length) - { - Throw.CustomSpanException(exceptionFactory, parameter, length); - } - - return parameter; - } - + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + public static bool IsOrDerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType, typeComparer); /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . + /// Checks if the string is a substring of the other string. /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is shorter than . + /// The string to be checked. + /// The other string. + /// True if is a substring of , else false. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) - { - if (parameter.Length < length) - { - Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); - } - - return parameter; - } - + [ContractAnnotation("value:null => halt; other:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf([NotNull][ValidatedNotNull] this string value, [NotNull][ValidatedNotNull] string other) => other.MustNotBeNull(nameof(other)).Contains(value); + // ReSharper restore RedundantNullableFlowAttribute /// - /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. + /// Checks if the string is a substring of the other string. /// - /// The span to be checked. - /// The value that the span must be longer than or equal to. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is shorter than . + /// The string to be checked. + /// The other string. + /// One of the enumeration values that specifies the rules for the search. + /// True if is a substring of , else false. + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) - { - if (parameter.Length < length) - { - Throw.CustomSpanException(exceptionFactory, parameter, length); - } - - return parameter; - } - + [ContractAnnotation("value:null => halt; other:null => halt")] + // ReSharper disable RedundantNullableFlowAttribute + public static bool IsSubstringOf([NotNull][ValidatedNotNull] this string value, [NotNull][ValidatedNotNull] string other, StringComparison comparisonType) => other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; /// - /// Ensures that the string is a valid file extension, or otherwise throws an . + /// Checks if the specified string is an email address using the default email regular expression + /// defined in . /// - /// The string to be checked. + /// The string to be checked if it is an email address. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddress:null => false")] + public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) => emailAddress != null && RegularExpressions.EmailRegex.IsMatch(emailAddress); + /// + /// Checks if the specified string is an email address using the provided regular expression for validation. + /// + /// The string to be checked. + /// The regular expression that determines whether the input string is an email address. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("emailAddress:null => false; emailAddressPattern:null => halt")] + public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) => emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); + /// + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should be approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not a valid file extension. - /// Thrown when is null. + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) - { - if (!parameter.MustNotBeNull(parameterName, message).IsFileExtension()) - { - Throw.NotFileExtension(parameter, parameterName, message); - } - - return parameter; - } - + public static double MustBeApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeApproximately(other, 0.0001, parameterName, message); /// - /// Ensures that the string is a valid file extension, or otherwise throws your custom exception. + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not a valid file extension. + /// The value to be checked. + /// The value that should be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static double MustBeApproximately(this double parameter, double other, Func exceptionFactory) { - if (parameter is null || !parameter.IsFileExtension()) + if (!parameter.IsApproximately(other)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the character span is a valid file extension, or otherwise throws a . + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws an . /// - /// The character span to be checked. + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// The original character span. - /// Thrown when is not a valid file extension. + /// + /// Thrown when the absolute difference between and is not + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeFileExtension(this Span parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + public static double MustBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - ((ReadOnlySpan)parameter).MustBeFileExtension(parameterName, message); - return parameter; - } + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); + } - /// - /// Ensures that the character span is a valid file extension, or otherwise throws your custom exception. - /// - /// The character span to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// The original character span. - /// Your custom exception thrown when is not a valid file extension. - public static Span MustBeFileExtension(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - ((ReadOnlySpan)parameter).MustBeFileExtension(exceptionFactory); return parameter; } /// - /// Ensures that the character memory is a valid file extension, or otherwise throws a . + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The character memory to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// The original character memory. - /// Thrown when is not a valid file extension. + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// Your custom exception thrown when the absolute difference between and + /// is not less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Memory MustBeFileExtension(this Memory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + public static double MustBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - ((ReadOnlySpan)parameter.Span).MustBeFileExtension(parameterName, message); - return parameter; - } + if (!parameter.IsApproximately(other, tolerance)) + { + Throw.CustomException(exceptionFactory, parameter, other, tolerance); + } - /// - /// Ensures that the character memory is a valid file extension, or otherwise throws your custom exception. - /// - /// The character memory to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// The original character memory. - /// Your custom exception thrown when is not a valid file extension. - public static Memory MustBeFileExtension(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) - { - ((ReadOnlySpan)parameter.Span).MustBeFileExtension(exceptionFactory); return parameter; } /// - /// Ensures that the read-only character memory is a valid file extension, or otherwise throws a . + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . /// - /// The read-only character memory to be checked. + /// The value to be checked. + /// The value that should be approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// The original read-only character memory. - /// Thrown when is not a valid file extension. + /// + /// Thrown when the absolute difference between and is + /// not less than 0.0001f. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlyMemory MustBeFileExtension(this ReadOnlyMemory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) - { - parameter.Span.MustBeFileExtension(parameterName, message); - return parameter; - } - + public static float MustBeApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeApproximately(other, 0.0001f, parameterName, message); /// - /// Ensures that the read-only character memory is a valid file extension, or otherwise throws your custom exception. + /// Ensures that the specified is approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . /// - /// The read-only character memory to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// The original read-only character memory. - /// Your custom exception thrown when is not a valid file extension. - public static ReadOnlyMemory MustBeFileExtension(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + /// The value to be checked. + /// The value that should be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is not + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeApproximately(this float parameter, float other, Func exceptionFactory) { - parameter.Span.MustBeFileExtension(exceptionFactory); + if (!parameter.IsApproximately(other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; } /// - /// Ensures that the read-only character span is a valid file extension, or otherwise throws a . + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws an . /// - /// The read-only character span to be checked. + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// The original read-only character span. - /// Thrown when is not a valid file extension. + /// + /// Thrown when the absolute difference between and is not + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeFileExtension(this ReadOnlySpan parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + public static float MustBeApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsFileExtension()) + if (!parameter.IsApproximately(other, tolerance)) { - Throw.NotFileExtension(parameter, parameterName, message); + Throw.MustBeApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the read-only character span is a valid file extension, or otherwise throws your custom exception. + /// Ensures that the specified is approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The read-only character span to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// The original read-only character span. - /// Your custom exception thrown when is not a valid file extension. - public static ReadOnlySpan MustBeFileExtension(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) + /// The value to be checked. + /// The value that should be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , , and + /// are passed to this delegate. + /// + /// + /// Your custom exception thrown when the absolute difference between and + /// is not less than . + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustBeApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - if (!parameter.IsFileExtension()) + if (!parameter.IsApproximately(other, tolerance)) { - Throw.CustomSpanException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } return parameter; } /// - /// Ensures that is not equal to using the default equality comparer, or otherwise throws a . + /// Ensures that the specified URI has the "http" scheme, or otherwise throws an . /// - /// The first value to be compared. - /// The other value to be compared. + /// The URI to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are equal. + /// Thrown when uses a different scheme than "http". + /// Thrown when is relative and thus has no scheme. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustNotBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (EqualityComparer.Default.Equals(parameter, other)) - { - Throw.ValuesEqual(parameter, other, parameterName, message); - } - - return parameter; - } - + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("http", parameterName, message); /// - /// Ensures that is not equal to using the default equality comparer, or otherwise throws your custom exception. + /// Ensures that the specified URI has the "http" scheme, or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The other value to be compared. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when and are equal. + /// The URI to be checked. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than "http", or when is a relative URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustNotBe(this T parameter, T other, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static Uri MustBeHttpUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("http", exceptionFactory); + /// + /// Checks if the specified span is empty or contains only white space characters. + /// + /// The span to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this Span span) => ((ReadOnlySpan)span).IsEmptyOrWhiteSpace(); + /// + /// Checks if the specified span is empty or contains only white space characters. + /// + /// The span to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this ReadOnlySpan span) { - if (EqualityComparer.Default.Equals(parameter, other)) + if (span.IsEmpty) { - Throw.CustomException(exceptionFactory, parameter, other); + return true; } - return parameter; + foreach (var character in span) + { + if (!character.IsWhiteSpace()) + { + return false; + } + } + + return true; } /// - /// Ensures that is not equal to using the specified equality comparer, or otherwise throws a . + /// Checks if the specified memory is empty or contains only white space characters. /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. + /// The memory to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this Memory memory) => memory.Span.IsEmptyOrWhiteSpace(); + /// + /// Checks if the specified memory is empty or contains only white space characters. + /// + /// The memory to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmptyOrWhiteSpace(this ReadOnlyMemory memory) => memory.Span.IsEmptyOrWhiteSpace(); + /// + /// Ensures that the collection has at most the specified number of items, or otherwise throws an . + /// + /// The collection to be checked. + /// The number of items the collection should have at most. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when and are equal. - /// Thrown when is null. + /// Thrown when does not contain at most the specified number of items. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMaximumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - if (equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) + if (parameter.Count(parameterName, message) > count) { - Throw.ValuesEqual(parameter, other, parameterName, message); + Throw.InvalidMaximumCollectionCount(parameter, count, parameterName, message); } return parameter; } /// - /// Ensures that is not equal to using the specified equality comparer, or otherwise throws your custom exception. + /// Ensures that the collection has at most the specified number of items, or otherwise throws your custom exception. /// - /// The first value to be compared. - /// The other value to be compared. - /// The equality comparer used for comparing the two values. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when and are equal, or when is null. + /// The collection to be checked. + /// The number of items the collection should have at most. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain at most the specified number of items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("equalityComparer:null => halt")] - public static T MustNotBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMaximumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (equalityComparer is null || equalityComparer.Equals(parameter, other)) + if (parameter is null || parameter.Count() > count) { - Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); + Throw.CustomException(exceptionFactory, parameter, count); } return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws a . + /// Ensures that the has at least the specified length, or otherwise throws an . /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// The to be checked. + /// The minimum length the should have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when has less than the specified length. + /// The default instance of will be treated as having length 0. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ImmutableArray MustHaveMinimumLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (string.Equals(parameter, other, comparisonType)) + var parameterLength = parameter.IsDefault ? 0 : parameter.Length; + if (parameterLength < length) { - Throw.ValuesEqual(parameter, other, parameterName, message); + Throw.InvalidMinimumImmutableArrayLength(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. + /// Ensures that the has at least the specified length, or otherwise throws your custom exception. /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// The to be checked. + /// The minimum length the should have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when has less than the specified length. + /// The default instance of will be treated as having length 0. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) + public static ImmutableArray MustHaveMinimumLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) { - if (string.Equals(parameter, other, comparisonType)) + var parameterLength = parameter.IsDefault ? 0 : parameter.Length; + if (parameterLength < length) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws a . + /// Checks if the specified GUID is an empty one. /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. + /// The GUID to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEmpty(this Guid parameter) => parameter == Guid.Empty; + /// + /// Ensures that the specified object reference is not null, or otherwise throws an . + /// + /// The object reference to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : class { - if (parameter.Equals(other, comparisonType)) + if (parameter is null) { - Throw.ValuesEqual(parameter, other, parameterName, message); + Throw.ArgumentNull(parameterName, message); } return parameter; } /// - /// Ensures that the two strings are not equal using the specified , or otherwise throws your custom exception. + /// Ensures that the specified object reference is not null, or otherwise throws your custom exception. /// - /// The first string to be compared. - /// The second string to be compared. - /// The enum value specifying how the two strings should be compared. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// Your custom exception thrown when is equal to . - /// Thrown when is not a valid value from the enum. + /// The reference to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string? MustNotBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeNull([NotNull, ValidatedNotNull, NoEnumeration] this T? parameter, Func exceptionFactory) + where T : class { - if (parameter.Equals(other, comparisonType)) + if (parameter is null) { - Throw.CustomException(exceptionFactory, parameter, other, comparisonType); + Throw.CustomException(exceptionFactory); } return parameter; } /// - /// Ensures that the collection has at least the specified number of items, or otherwise throws an . + /// Ensures that the has the specified scheme, or otherwise throws an . /// - /// The collection to be checked. - /// The number of items the collection should have at least. + /// The URI to be checked. + /// The scheme that the URI should have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain at least the specified number of items. + /// Thrown when uses a different scheme than the specified one. + /// Thrown when is relative and thus has no scheme. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Count(parameterName, message) < count) + if (string.Equals(parameter.MustBeAbsoluteUri(parameterName, message).Scheme, scheme) == false) { - Throw.InvalidMinimumCollectionCount(parameter, count, parameterName, message); + Throw.UriMustHaveScheme(parameter, scheme, parameterName, message); } return parameter; } /// - /// Ensures that the collection has at least the specified number of items, or otherwise throws your custom exception. + /// Ensures that the has the specified scheme, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The number of items the collection should have at least. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. + /// The URI to be checked. + /// The scheme that the URI should have. + /// The delegate that creates the exception to be thrown. is passed to this delegate. + /// Your custom exception thrown when uses a different scheme than the specified one, or when is a relative URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustHaveMinimumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) - where TCollection : class, IEnumerable + public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) { - if (parameter is null || parameter.Count() < count) + if (string.Equals(parameter.MustBeAbsoluteUri(exceptionFactory).Scheme, scheme) == false) { - Throw.CustomException(exceptionFactory, parameter, count); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// Ensures that the has the specified scheme, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is greater than . - /// Thrown when is null. + /// The URI to be checked. + /// The scheme that the URI should have. + /// The delegate that creates the exception to be thrown. and are passed to this delegate. + /// Your custom exception thrown when uses a different scheme than the specified one, or when is a relative URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static Uri MustHaveScheme([NotNull][ValidatedNotNull] this Uri? parameter, string scheme, Func exceptionFactory) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) + if (parameter is null || !parameter.IsAbsoluteUri || parameter.Scheme.Equals(scheme) == false) { - Throw.MustBeLessThanOrEqualTo(parameter, other, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, scheme); } return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// Ensures that the specified parameter is not null when is a reference type, or otherwise + /// throws an . PLEASE NOTICE: you should only use this assertion in generic contexts, + /// use by default. /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is greater than , or when is null. + /// The value to be checked for null. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is a reference type and is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) > 0) + if (default(T) != null) { - Throw.CustomException(exceptionFactory, parameter!, other); - } + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - return parameter; - } + return parameter; +#pragma warning restore CS8777 + } - /// - /// Ensures that the specified is less than the given value, or otherwise throws an . - /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is not less than . - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable - { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) + if (parameter is null) { - Throw.MustBeLessThan(parameter, other, parameterName, message); + Throw.ArgumentNull(parameterName, message); } return parameter; } /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified parameter is not null when is a reference type, or otherwise + /// throws your custom exception. PLEASE NOTICE: you should only use this assertion in generic contexts, + /// use by default. /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is not less than , or when is null. + /// The value to be checked for null. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is a reference type and is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) >= 0) + if (default(T) != null) { - Throw.CustomException(exceptionFactory, parameter!, other); + // If we end up here, parameter cannot be null +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + + return parameter; +#pragma warning restore CS8777 + } + + if (parameter is null) + { + Throw.CustomException(exceptionFactory); } return parameter; } /// - /// Ensures that the value is not one of the specified items, or otherwise throws a . + /// Ensures that the specified uses , or otherwise throws an . /// - /// The value to be checked. - /// The items that must not contain the value. + /// The date time to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is equal to one of the specified . - /// Thrown when is null. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests - public static TItem MustNotBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static DateTime MustBeUnspecified(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - // ReSharper disable PossibleMultipleEnumeration - if (parameter.IsOneOf(items.MustNotBeNull(nameof(items), message))) + if (parameter.Kind != DateTimeKind.Unspecified) { - Throw.ValueIsOneOf(parameter, items, parameterName, message); + Throw.MustBeUnspecifiedDateTime(parameter, parameterName, message); } return parameter; - // ReSharper restore PossibleMultipleEnumeration } /// - /// Ensures that the value is not one of the specified items, or otherwise throws your custom exception. + /// Ensures that the specified uses , or otherwise throws your custom exception. /// - /// The value to be checked. - /// The items that must not contain the value. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is equal to one of the specified , or when is null. + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("items:null => halt")] - public static TItem MustNotBeOneOf(this TItem parameter, [NotNull][ValidatedNotNull] TCollection items, Func exceptionFactory) - where TCollection : class, IEnumerable + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUnspecified(this DateTime parameter, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (items is null || parameter.IsOneOf(items)) + if (parameter.Kind != DateTimeKind.Unspecified) { - Throw.CustomException(exceptionFactory, parameter, items!); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Checks if the specified span is empty or contains only white space characters. + /// Checks if the specified string represents a valid file extension. /// - /// The span to be checked. + /// + /// The string to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the string is a valid file extension, false otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmptyOrWhiteSpace(this Span span) => ((ReadOnlySpan)span).IsEmptyOrWhiteSpace(); + public static bool IsFileExtension([NotNullWhen(true)] this string? value) => value != null && IsFileExtension(value.AsSpan()); /// - /// Checks if the specified span is empty or contains only white space characters. + /// Checks if the specified character span represents a valid file extension. /// - /// The span to be checked. + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmptyOrWhiteSpace(this ReadOnlySpan span) + public static bool IsFileExtension(this Span value) => IsFileExtension((ReadOnlySpan)value); + /// + /// Checks if the specified character memory represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this ReadOnlyMemory value) => IsFileExtension(value.Span); + /// + /// Checks if the specified character memory represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this Memory value) => IsFileExtension(value.Span); + /// + /// Checks if the specified character span represents a valid file extension. + /// + /// + /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, + /// and additional periods. + /// + /// True if the span is a valid file extension, false otherwise. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsFileExtension(this ReadOnlySpan value) { - if (span.IsEmpty) + // ReSharper disable once UseIndexFromEndExpression -- cannot use index from end expression in .NET Standard 2.0 + if (value.Length <= 1 || value[0] != '.' || value[value.Length - 1] == '.') { - return true; + return false; } - foreach (var character in span) + var hasAlphanumeric = false; + for (var i = 1; i < value.Length; i++) { - if (!character.IsWhiteSpace()) + var character = value[i]; + if (character.IsLetterOrDigit()) + { + hasAlphanumeric = true; + } + else if (character != '.') { return false; } } - return true; + return hasAlphanumeric; } /// - /// Checks if the specified memory is empty or contains only white space characters. + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. /// - /// The memory to be checked. + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is less than or if their absolute difference + /// is smaller than the given , otherwise false. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmptyOrWhiteSpace(this Memory memory) => memory.Span.IsEmptyOrWhiteSpace(); + public static bool IsLessThanOrApproximately(this double value, double other, double tolerance) => value < other || value.IsApproximately(other, tolerance); /// - /// Checks if the specified memory is empty or contains only white space characters. + /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001. /// - /// The memory to be checked. + /// The first value to compare. + /// The second value to compare. + /// + /// True if is less than or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsEmptyOrWhiteSpace(this ReadOnlyMemory memory) => memory.Span.IsEmptyOrWhiteSpace(); + public static bool IsLessThanOrApproximately(this double value, double other) => value < other || value.IsApproximately(other); /// - /// Ensures that the specified is less than the given value, or otherwise throws an . + /// Checks if the specified value is less than or approximately the same as the other value, using the given tolerance. /// - /// The comparable to be checked. - /// The boundary value that must be greater than . + /// The first value to compare. + /// The second value to compare. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if is less than or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this float value, float other, float tolerance) => value < other || value.IsApproximately(other, tolerance); + /// + /// Checks if the specified value is less than or approximately the same as the other value, using the default tolerance of 0.0001f. + /// + /// The first value to compare. + /// The second value to compare. + /// + /// True if is less than or if their absolute difference + /// is smaller than 0.0001f, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsLessThanOrApproximately(this float value, float other) => value < other || value.IsApproximately(other); + /// + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is not less than . - /// Thrown when is null. + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static double MustNotBeApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustNotBeApproximately(other, 0.0001, parameterName, message); + /// + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double MustNotBeApproximately(this double parameter, double other, Func exceptionFactory) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) >= 0) + if (parameter.IsApproximately(other)) { - Throw.MustNotBeGreaterThanOrEqualTo(parameter, other, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the specified is less than the given value, or otherwise throws your custom exception. + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be greater than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is not less than , or when is null. + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static double MustNotBeApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) >= 0) + if (parameter.IsApproximately(other, tolerance)) { - Throw.CustomException(exceptionFactory, parameter!, other); + Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the specified enum value is valid, or otherwise throws an . An enum value - /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type - /// is marked with the . + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The type of the enum. /// The value to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is no valid enum value. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// Your custom exception thrown when the absolute difference between and + /// is less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T MustBeValidEnumValue(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : struct, Enum + public static double MustNotBeApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) { - if (!EnumInfo.IsValidEnumValue(parameter)) + if (parameter.IsApproximately(other, tolerance)) { - Throw.EnumValueNotDefined(parameter, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } return parameter; } /// - /// Ensures that the specified enum value is valid, or otherwise throws your custom exception. An enum value - /// is valid when the specified value is one of the constants defined in the enum, or a valid flags combination when the enum type - /// is marked with the . + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001f, or otherwise throws an + /// . /// - /// The type of the enum. /// The value to be checked. - /// The delegate that creates your custom exception. The is passed to this delegate. - /// Your custom exception thrown when is no valid enum value, or when is no enum type. + /// The value that should not be approximately equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001f. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static T MustBeValidEnumValue(this T parameter, Func exceptionFactory) - where T : struct, Enum + public static float MustNotBeApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustNotBeApproximately(other, 0.0001f, parameterName, message); + /// + /// Ensures that the specified is not approximately equal to the given + /// value, using the default tolerance of 0.0001, or otherwise throws an + /// . + /// + /// The value to be checked. + /// The value that should not be approximately equal to. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// + /// + /// Thrown when the absolute difference between and is + /// less than 0.0001. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static float MustNotBeApproximately(this float parameter, float other, Func exceptionFactory) { - if (!EnumInfo.IsValidEnumValue(parameter)) + if (parameter.IsApproximately(other)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, other); } return parameter; } /// - /// Ensures that the specified GUID is not empty, or otherwise throws an . + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws an . /// - /// The GUID to be checked. + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an empty GUID. + /// + /// Thrown when the absolute difference between and is + /// less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Guid MustNotBeEmpty(this Guid parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static float MustNotBeApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter == Guid.Empty) + if (parameter.IsApproximately(other, tolerance)) { - Throw.EmptyGuid(parameterName, message); + Throw.MustNotBeApproximately(parameter, other, tolerance, parameterName, message); } return parameter; } /// - /// Ensures that the specified GUID is not empty, or otherwise throws your custom exception. + /// Ensures that the specified is not approximately equal to the given + /// value, or otherwise throws your custom exception. /// - /// The GUID to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is an empty GUID. + /// The value to be checked. + /// The value that should not be approximately equal to. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// The delegate that creates your custom exception. , , and + /// are passed to this delegate. + /// + /// + /// Your custom exception thrown when the absolute difference between and + /// is less than . + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static Guid MustNotBeEmpty(this Guid parameter, Func exceptionFactory) + public static float MustNotBeApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) { - if (parameter == Guid.Empty) + if (parameter.IsApproximately(other, tolerance)) { - Throw.CustomException(exceptionFactory); + Throw.CustomException(exceptionFactory, parameter, other, tolerance); } return parameter; @@ -3824,304 +3905,278 @@ public static bool Equals(this string? @string, string? value, StringComparisonT } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws an . + /// Checks if the value is not within the specified range. /// /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . + /// The range where must not be in-between. + /// True if the parameter is not within the specified range, else false. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsNotIn([NotNull][ValidatedNotNull] this T parameter, Range range) + where T : IComparable => !range.IsValueWithinRange(parameter); + /// + /// Ensures that is equal to using the default equality comparer, or otherwise throws a . + /// + /// The first value to be compared. + /// The other value to be compared. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is greater than . - /// Thrown when is null. + /// Thrown when and are not equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static T MustBe(this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) - Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); + if (!EqualityComparer.Default.Equals(parameter, other)) + { + Throw.ValuesNotEqual(parameter, other, parameterName, message); + } + return parameter; } /// - /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// Ensures that is equal to using the default equality comparer, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be greater than or equal to . + /// The first value to be compared. + /// The other value to be compared. /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is greater than , or when is null. + /// Your custom exception thrown when and are not equal. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static T MustBe(this T parameter, T other, Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) > 0) - Throw.CustomException(exceptionFactory, parameter!, other); + if (!EqualityComparer.Default.Equals(parameter, other)) + { + Throw.CustomException(exceptionFactory, parameter, other); + } + return parameter; } /// - /// Checks if the given is equal to the specified or if it derives from it or implements it. - /// Internally, this method uses so that constructed generic types and their corresponding generic type definitions - /// are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrInheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); - /// - /// Checks if the given is equal to the specified or if it derives from it or implements it. - /// This overload uses the specified to compare the types. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// The equality comparer used to compare the types. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - public static bool IsOrInheritsFrom( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer); - /// - /// Checks if the given is equal to the specified or if it derives from it. Internally, this - /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// Thrown when or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt")] - public static bool IsOrDerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType); - /// - /// Checks if the given is equal to the specified or if it derives from it. This overload uses the specified - /// to compare the types. - /// - /// The type to be checked. - /// The type that is equivalent to or the base class type where derives from. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] - public static bool IsOrDerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.DerivesFrom(otherType, typeComparer); - /// - /// Ensures that the string is not null and trimmed at the start, or otherwise throws a . - /// Empty strings are regarded as trimmed. + /// Ensures that is equal to using the specified equality comparer, or otherwise throws a . /// - /// The string to be checked. + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not trimmed at the start, i.e. they start with white space characters. - /// Empty strings are regarded as trimmed. - /// - /// Thrown when is null. + /// Thrown when and are not equal. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtStart()) + if (!equalityComparer.MustNotBeNull(nameof(equalityComparer), message).Equals(parameter, other)) { - Throw.NotTrimmedAtStart(parameter, parameterName, message); + Throw.ValuesNotEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string is not null and trimmed at the start, or otherwise throws your custom exception. - /// Empty strings are regarded as trimmed. + /// Ensures that is equal to using the specified equality comparer, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed at the start. Empty strings are regarded as trimmed. + /// The first value to be compared. + /// The other value to be compared. + /// The equality comparer used for comparing the two values. + /// The delegate that creates your custom exception. , , and are passed to this delegate. + /// Your custom exception thrown when and are not equal, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtStart([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + [ContractAnnotation("equalityComparer:null => halt")] + public static T MustBe(this T parameter, T other, IEqualityComparer equalityComparer, Func, Exception> exceptionFactory) { - if (parameter is null || !parameter.AsSpan().IsTrimmedAtStart()) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (equalityComparer is null || !equalityComparer.Equals(parameter, other)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, other, equalityComparer!); } return parameter; } /// - /// Checks if the type implements the specified interface type. Internally, this method uses - /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. - /// - /// The type to be checked. - /// The interface type that should implement. - /// Thrown when or is null. - [ContractAnnotation("type:null => halt; interfaceType:null => halt")] - public static bool Implements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type interfaceType) - { - type.MustNotBeNull(); - interfaceType.MustNotBeNull(); - var implementedInterfaces = type.GetInterfaces(); - for (var i = 0; i < implementedInterfaces.Length; ++i) - { - if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) - { - return true; - } - } - - return false; - } - - /// - /// Checks if the type implements the specified interface type. This overload uses the specified - /// to compare the interface types. - /// - /// The type to be checked. - /// The interface type that should implement. - /// The equality comparer used to compare the interface types. - /// Thrown when , or , or is null. - [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] - public static bool Implements( -#if NET8_0 - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif - [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type interfaceType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) - { - type.MustNotBeNull(); - interfaceType.MustNotBeNull(); - typeComparer.MustNotBeNull(); - var implementedInterfaces = type.GetInterfaces(); - for (var i = 0; i < implementedInterfaces.Length; ++i) - { - if (typeComparer.Equals(implementedInterfaces[i], interfaceType)) - { - return true; - } - } - - return false; - } - - /// - /// Ensures that the specified URI is a relative one, or otherwise throws an . + /// Ensures that the two strings are equal using the specified , or otherwise throws a . /// - /// The URI to be checked. + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is an absolute URI. - /// Thrown when is null. + /// Thrown when is not equal to . + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri) + if (!string.Equals(parameter, other, comparisonType)) { - Throw.MustBeRelativeUri(parameter, parameterName, message); + Throw.ValuesNotEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the specified URI is a relative one, or otherwise throws your custom exception. + /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. /// - /// The URI to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is an absolute URI, or when is null. + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to . + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeRelativeUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) + public static string? MustBe(this string? parameter, string? other, StringComparison comparisonType, Func exceptionFactory) { - if (parameter is null || parameter.IsAbsoluteUri) + if (!string.Equals(parameter, other, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); } return parameter; } /// - /// Ensures that the specified uses , or otherwise throws an . + /// Ensures that the two strings are equal using the specified , or otherwise throws a . /// - /// The date time to be checked. + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not use . + /// Thrown when is not equal to . + /// Thrown when is not a valid value from the enum. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static DateTime MustBeUtc(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Kind != DateTimeKind.Utc) + if (!parameter.Equals(other, comparisonType)) { - Throw.MustBeUtcDateTime(parameter, parameterName, message); + Throw.ValuesNotEqual(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the specified uses , or otherwise throws your custom exception. + /// Ensures that the two strings are equal using the specified , or otherwise throws your custom exception. /// - /// The date time to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when does not use . - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static DateTime MustBeUtc(this DateTime parameter, Func exceptionFactory) + /// The first string to be compared. + /// The second string to be compared. + /// The enum value specifying how the two strings should be compared. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is not equal to . + /// Thrown when is not a valid value from the enum. + public static string? MustBe(this string? parameter, string? other, StringComparisonType comparisonType, Func exceptionFactory) { - if (parameter.Kind != DateTimeKind.Utc) + if (!parameter.Equals(other, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter); + Throw.CustomException(exceptionFactory, parameter, other, comparisonType); } return parameter; } /// - /// Checks if the specified character is a white space character. + /// Checks if the value is within the specified range. /// + /// The comparable to be checked. + /// The range where must be in-between. + /// True if the parameter is within the specified range, else false. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsWhiteSpace(this char character) => char.IsWhiteSpace(character); + public static bool IsIn([NotNull][ValidatedNotNull] this T parameter, Range range) + where T : IComparable => range.IsValueWithinRange(parameter); /// - /// Ensures that the string is either "\n" or "\r\n", or otherwise throws a . This is done independently of the current value of . + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// + /// The first value to be compared. + /// The second value to be compared. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if and are equal or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this double value, double other, double tolerance) => Math.Abs(value - other) <= tolerance; + /// + /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001. + /// + /// The first value to be compared. + /// The second value to be compared. + /// + /// True if and are equal or if their absolute difference + /// is smaller than 0.0001, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this double value, double other) => Math.Abs(value - other) <= 0.0001; + /// + /// Checks if the specified value is approximately the same as the other value, using the given tolerance. + /// + /// The first value to be compared. + /// The second value to be compared. + /// The tolerance indicating how much the two values may differ from each other. + /// + /// True if and are equal or if their absolute difference + /// is smaller than the given , otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this float value, float other, float tolerance) => Math.Abs(value - other) <= tolerance; + /// + /// Checks if the specified value is approximately the same as the other value, using the default tolerance of 0.0001f. + /// + /// The first value to be compared. + /// The second value to be compared. + /// + /// True if and are equal or if their absolute difference + /// is smaller than 0.0001f, otherwise false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsApproximately(this float value, float other) => Math.Abs(value - other) <= 0.0001f; + /// + /// Checks if the specified collection is null or empty. + /// + /// The collection to be checked. + /// True if the collection is null or empty, else false. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] + public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => collection is null || collection.Count() == 0; + /// + /// Checks if the specified string is null or empty. + /// + /// The string to be checked. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] + public static bool IsNullOrEmpty([NotNullWhen(false)] this string? @string) => string.IsNullOrEmpty(@string); + /// + /// Ensures that the string is a valid file extension, or otherwise throws an . /// /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is not equal to "\n" or "\r\n". + /// Thrown when is not a valid file extension. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!parameter.MustNotBeNull(parameterName, message).IsNewLine()) + if (!parameter.MustNotBeNull(parameterName, message).IsFileExtension()) { - Throw.NotNewLine(parameter, parameterName, message); + Throw.NotFileExtension(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the string is either "\n" or "\r\n", or otherwise throws your custom exception. This is done independently of the current value of . + /// Ensures that the string is a valid file extension, or otherwise throws your custom exception. /// /// The string to be checked. /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is not equal to "\n" or "\r\n". + /// Your custom exception thrown when is null or not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static string MustBeFileExtension([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsNewLine()) + if (parameter is null || !parameter.IsFileExtension()) { Throw.CustomException(exceptionFactory, parameter); } @@ -4130,321 +4185,300 @@ public static string MustBeNewLine([NotNull][ValidatedNotNull] this string? para } /// - /// Checks if the specified string represents a valid file extension. - /// - /// - /// The string to be checked. It must start with a period (.) and can only contain letters, digits, - /// and additional periods. - /// - /// True if the string is a valid file extension, false otherwise. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFileExtension([NotNullWhen(true)] this string? value) => value != null && IsFileExtension(value.AsSpan()); - /// - /// Checks if the specified character span represents a valid file extension. + /// Ensures that the character span is a valid file extension, or otherwise throws a . /// - /// - /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, - /// and additional periods. - /// - /// True if the span is a valid file extension, false otherwise. + /// The character span to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The original character span. + /// Thrown when is not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFileExtension(this Span value) => IsFileExtension((ReadOnlySpan)value); + public static Span MustBeFileExtension(this Span parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter).MustBeFileExtension(parameterName, message); + return parameter; + } + /// - /// Checks if the specified character memory represents a valid file extension. + /// Ensures that the character span is a valid file extension, or otherwise throws your custom exception. /// - /// - /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, - /// and additional periods. - /// - /// True if the span is a valid file extension, false otherwise. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFileExtension(this ReadOnlyMemory value) => IsFileExtension(value.Span); - /// - /// Checks if the specified character memory represents a valid file extension. - /// - /// - /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, - /// and additional periods. - /// - /// True if the span is a valid file extension, false otherwise. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFileExtension(this Memory value) => IsFileExtension(value.Span); - /// - /// Checks if the specified character span represents a valid file extension. - /// - /// - /// The character span to be checked. It must start with a period (.) and can only contain letters, digits, - /// and additional periods. - /// - /// True if the span is a valid file extension, false otherwise. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsFileExtension(this ReadOnlySpan value) + /// The character span to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original character span. + /// Your custom exception thrown when is not a valid file extension. + public static Span MustBeFileExtension(this Span parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - // ReSharper disable once UseIndexFromEndExpression -- cannot use index from end expression in .NET Standard 2.0 - if (value.Length <= 1 || value[0] != '.' || value[value.Length - 1] == '.') - { - return false; - } - - var hasAlphanumeric = false; - for (var i = 1; i < value.Length; i++) - { - var character = value[i]; - if (character.IsLetterOrDigit()) - { - hasAlphanumeric = true; - } - else if (character != '.') - { - return false; - } - } - - return hasAlphanumeric; + ((ReadOnlySpan)parameter).MustBeFileExtension(exceptionFactory); + return parameter; } /// - /// Checks if the string is either "\n" or "\r\n". This is done independently of the current value of . - /// - /// The string to be checked. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, parameter:canbenull; => true, parameter:notnull")] - public static bool IsNewLine([NotNullWhen(true)] this string? parameter) => parameter == "\n" || parameter == "\r\n"; - /// - /// Checks if the given is a generic type that has open generic parameters, - /// but is no generic type definition. - /// - /// The type to be checked. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("type:null => halt")] - // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static bool IsOpenConstructedGenericType([NotNull][ValidatedNotNull] this Type type) => type.MustNotBeNull(nameof(type)).IsGenericType && type.ContainsGenericParameters && type.IsGenericTypeDefinition == false; - /// - /// Checks if the specified string is an email address using the default email regular expression - /// defined in . - /// - /// The string to be checked if it is an email address. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("emailAddress:null => false")] - public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress) => emailAddress != null && RegularExpressions.EmailRegex.IsMatch(emailAddress); - /// - /// Checks if the specified string is an email address using the provided regular expression for validation. - /// - /// The string to be checked. - /// The regular expression that determines whether the input string is an email address. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("emailAddress:null => false; emailAddressPattern:null => halt")] - public static bool IsEmailAddress([NotNullWhen(true)] this string? emailAddress, Regex emailAddressPattern) => emailAddress != null && emailAddressPattern.MustNotBeNull(nameof(emailAddressPattern)).IsMatch(emailAddress); - /// - /// Checks if the specified character is a letter. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsLetter(this char character) => char.IsLetter(character); - /// - /// Checks if the string is a substring of the other string. + /// Ensures that the character memory is a valid file extension, or otherwise throws a . /// - /// The string to be checked. - /// The other string. - /// True if is a substring of , else false. - /// Thrown when or is null. + /// The character memory to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// The original character memory. + /// Thrown when is not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("value:null => halt; other:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute - public static bool IsSubstringOf([NotNull][ValidatedNotNull] this string value, [NotNull][ValidatedNotNull] string other) => other.MustNotBeNull(nameof(other)).Contains(value); - // ReSharper restore RedundantNullableFlowAttribute + public static Memory MustBeFileExtension(this Memory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + { + ((ReadOnlySpan)parameter.Span).MustBeFileExtension(parameterName, message); + return parameter; + } + /// - /// Checks if the string is a substring of the other string. + /// Ensures that the character memory is a valid file extension, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string. - /// One of the enumeration values that specifies the rules for the search. - /// True if is a substring of , else false. - /// Thrown when or is null. - /// Thrown when is not a valid value. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("value:null => halt; other:null => halt")] - // ReSharper disable RedundantNullableFlowAttribute - public static bool IsSubstringOf([NotNull][ValidatedNotNull] this string value, [NotNull][ValidatedNotNull] string other, StringComparison comparisonType) => other.MustNotBeNull(nameof(other)).IndexOf(value, comparisonType) != -1; + /// The character memory to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original character memory. + /// Your custom exception thrown when is not a valid file extension. + public static Memory MustBeFileExtension(this Memory parameter, ReadOnlySpanExceptionFactory exceptionFactory) + { + ((ReadOnlySpan)parameter.Span).MustBeFileExtension(exceptionFactory); + return parameter; + } + /// - /// Ensures that the string's length is within the specified range, or otherwise throws a . + /// Ensures that the read-only character memory is a valid file extension, or otherwise throws a . /// - /// The string to be checked. - /// The range where the string's length must be in-between. + /// The read-only character memory to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the length of is not with the specified . - /// Thrown when is null. + /// The original read-only character memory. + /// Thrown when is not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlyMemory MustBeFileExtension(this ReadOnlyMemory parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!range.IsValueWithinRange(parameter.MustNotBeNull(parameterName, message).Length)) - { - Throw.StringLengthNotInRange(parameter, range, parameterName, message); - } - + parameter.Span.MustBeFileExtension(parameterName, message); return parameter; } /// - /// Ensures that the string's length is within the specified range, or otherwise throws your custom exception. + /// Ensures that the read-only character memory is a valid file extension, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The range where the string's length must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or its length is not within the specified range. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLengthIn([NotNull][ValidatedNotNull] this string? parameter, Range range, Func, Exception> exceptionFactory) + /// The read-only character memory to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original read-only character memory. + /// Your custom exception thrown when is not a valid file extension. + public static ReadOnlyMemory MustBeFileExtension(this ReadOnlyMemory parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter is null || !range.IsValueWithinRange(parameter.Length)) - { - Throw.CustomException(exceptionFactory, parameter, range); - } - + parameter.Span.MustBeFileExtension(exceptionFactory); return parameter; } /// - /// Ensures that the 's length is within the specified range, or otherwise throws an . + /// Ensures that the read-only character span is a valid file extension, or otherwise throws a . /// - /// The to be checked. - /// The range where the 's length must be in-between. + /// The read-only character span to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the length of is not within the specified . + /// The original read-only character span. + /// Thrown when is not a valid file extension. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveLengthIn(this ImmutableArray parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustBeFileExtension(this ReadOnlySpan parameter, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - var length = parameter.IsDefault ? 0 : parameter.Length; - if (!range.IsValueWithinRange(length)) + if (!parameter.IsFileExtension()) { - Throw.ImmutableArrayLengthNotInRange(parameter, range, parameterName, message); + Throw.NotFileExtension(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the 's length is within the specified range, or otherwise throws your custom exception. + /// Ensures that the read-only character span is a valid file extension, or otherwise throws your custom exception. /// - /// The to be checked. - /// The range where the 's length must be in-between. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the length of is not within the specified range. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("exceptionFactory:null => halt")] - public static ImmutableArray MustHaveLengthIn(this ImmutableArray parameter, Range range, Func, Range, Exception> exceptionFactory) + /// The read-only character span to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// The original read-only character span. + /// Your custom exception thrown when is not a valid file extension. + public static ReadOnlySpan MustBeFileExtension(this ReadOnlySpan parameter, ReadOnlySpanExceptionFactory exceptionFactory) { - var length = parameter.IsDefault ? 0 : parameter.Length; - if (!range.IsValueWithinRange(length)) + if (!parameter.IsFileExtension()) { - Throw.CustomException(exceptionFactory, parameter, range); + Throw.CustomSpanException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified parameter is not the default value, or otherwise throws an - /// for reference types, or an for value types. + /// Checks if the given is one of the specified . /// - /// The value to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when is a reference type and null. - /// Thrown when is a value type and the default value. + /// The item to be checked. + /// The collection that might contain the . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("items:null => halt")] + // ReSharper disable once RedundantNullableFlowAttribute - the attribute has an effect, see Issue72NotNullAttribute tests + public static bool IsOneOf(this TItem item, [NotNull][ValidatedNotNull] IEnumerable items) { - if (default(T)is null) + if (items is ICollection collection) { - if (parameter is null) - { - Throw.ArgumentNull(parameterName, message); - } - - return parameter; + return collection.Contains(item); } - if (EqualityComparer.Default.Equals(parameter, default !)) + if (items is string @string && item is char character) { - Throw.ArgumentDefault(parameterName, message); + return @string.IndexOf(character) != -1; } -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - - return parameter; -#pragma warning restore CS8777 + return items.MustNotBeNull(nameof(items)).ContainsViaForeach(item); } /// - /// Ensures that the specified parameter is not the default value, or otherwise throws your custom exception. + /// Ensures that the specified is not greater than the given value, or otherwise throws an . /// - /// The value to be checked. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is the default value. + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is greater than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (default(T)is null) - { - if (parameter is null) - { - Throw.CustomException(exceptionFactory); - } + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) > 0) + Throw.MustNotBeGreaterThan(parameter, other, parameterName, message); + return parameter; + } - return parameter; + /// + /// Ensures that the specified is not greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be greater than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is greater than , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeGreaterThan([NotNull, ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) > 0) + Throw.CustomException(exceptionFactory, parameter!, other); + return parameter; + } + + /// + /// Ensures that the specified nullable has a value and returns it, or otherwise throws a . + /// + /// The nullable to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when has no value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : struct + { + if (!parameter.HasValue) + { + Throw.NullableHasNoValue(parameterName, message); } - if (EqualityComparer.Default.Equals(parameter, default !)) + return parameter.Value; + } + + /// + /// Ensures that the specified nullable has a value and returns it, or otherwise throws your custom exception. + /// + /// The nullable to be checked. + /// The delegate that creates your custom exception. + /// Thrown when has no value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("exceptionFactory:null => halt")] + public static T MustHaveValue([NotNull, NoEnumeration] this T? parameter, Func exceptionFactory) + where T : struct + { + if (!parameter.HasValue) { Throw.CustomException(exceptionFactory); } -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter.Value; + } + + /// + /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than or equal to . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable + { + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) + { + Throw.MustNotBeLessThanOrEqualTo(parameter, other, parameterName, message); + } return parameter; -#pragma warning restore CS8777 } /// - /// Ensures that the string is shorter than the specified length, or otherwise throws a . + /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// + /// The comparable to be checked. + /// The boundary value that must be less than . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than or equal to , or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeLessThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable + { + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) <= 0) + { + Throw.CustomException(exceptionFactory, parameter!, other); + } + + return parameter; + } + + /// + /// Ensures that the string has the specified length, or otherwise throws a . /// /// The string to be checked. - /// The length that the string must be shorter than. + /// The asserted length of the string. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length greater than or equal to . + /// Thrown when has a length other than . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustHaveLength([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).Length >= length) + if (parameter.MustNotBeNull(parameterName, message).Length != length) { - Throw.StringNotShorterThan(parameter, length, parameterName, message); + Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the string is shorter than the specified length, or otherwise throws your custom exception. + /// Ensures that the string has the specified length, or otherwise throws your custom exception. /// /// The string to be checked. - /// The length that the string must be shorter than. + /// The asserted length of the string. /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length greater than or equal to . + /// Your custom exception thrown when is null or when it has a length other than . [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + public static string MustHaveLength([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (parameter is null || parameter.Length >= length) + if (parameter is null || parameter.Length != length) { Throw.CustomException(exceptionFactory, parameter, length); } @@ -4453,31 +4487,31 @@ public static string MustBeShorterThan([NotNull][ValidatedNotNull] this string? } /// - /// Ensures that the span is shorter than the specified length, or otherwise throws an . + /// Ensures that the span has the specified length, or otherwise throws an . /// /// The span to be checked. - /// The length value that the span must be shorter than. + /// The length that the span must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than or equal to . + /// Thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustHaveLength(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - ((ReadOnlySpan)parameter).MustBeShorterThan(length, parameterName, message); + ((ReadOnlySpan)parameter).MustHaveLength(length, parameterName, message); return parameter; } /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// /// The span to be checked. - /// The length value that the span must be shorter than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than or equal to . + /// The length that the span must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + public static Span MustHaveLength(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (parameter.Length >= length) + if (parameter.Length != length) { Throw.CustomSpanException(exceptionFactory, parameter, length); } @@ -4486,35 +4520,35 @@ public static Span MustBeShorterThan(this Span parameter, int length, S } /// - /// Ensures that the span is shorter than the specified length, or otherwise throws an . + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// /// The span to be checked. - /// The length value that the span must be shorter than. + /// The length that the span must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is longer than or equal to . + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Length >= length) + if (parameter.Length != length) { - Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); + Throw.InvalidSpanLength(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. + /// Ensures that the span has the specified length, or otherwise throws your custom exception. /// /// The span to be checked. - /// The length value that the span must be shorter than. - /// The delegate that creates your custom exception. and are passed to it. - /// Your custom exception thrown when is longer than or equal to . + /// The length that the span must have. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter.Length >= length) + if (parameter.Length != length) { Throw.CustomSpanException(exceptionFactory, parameter, length); } @@ -4523,39 +4557,37 @@ public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan paramete } /// - /// Ensures that the has at most the specified length, or otherwise throws an . + /// Ensures that the immutable array has the specified length, or otherwise throws an . /// - /// The to be checked. - /// The maximum length the should have. + /// The immutable array to be checked. + /// The length that the immutable array must have. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has more than the specified length. - /// The default instance of will be treated as having length 0. + /// Thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveMaximumLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ImmutableArray MustHaveLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - var parameterLength = parameter.IsDefault ? 0 : parameter.Length; - if (parameterLength > length) + var actualLength = parameter.IsDefault ? 0 : parameter.Length; + if (actualLength != length) { - Throw.InvalidMaximumImmutableArrayLength(parameter, length, parameterName, message); + Throw.InvalidImmutableArrayLength(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the has at most the specified length, or otherwise throws your custom exception. + /// Ensures that the immutable array has the specified length, or otherwise throws your custom exception. /// - /// The to be checked. - /// The maximum length the should have. + /// The immutable array to be checked. + /// The length that the immutable array must have. /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when has more than the specified length. - /// The default instance of will be treated as having length 0. + /// Your custom exception thrown when does not have the specified length. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveMaximumLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) + public static ImmutableArray MustHaveLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) { - var parameterLength = parameter.IsDefault ? 0 : parameter.Length; - if (parameterLength > length) + var actualLength = parameter.IsDefault ? 0 : parameter.Length; + if (actualLength != length) { Throw.CustomException(exceptionFactory, parameter, length); } @@ -4564,77 +4596,65 @@ public static ImmutableArray MustHaveMaximumLength(this ImmutableArray } /// - /// Checks if the specified collection is null or empty. + /// Checks if the given type derives from the specified base class or interface type. Internally, this method uses + /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The collection to be checked. - /// True if the collection is null or empty, else false. + /// The type to be checked. + /// The type describing an interface or base class that should derive from or implement. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> true, collection:canbenull; => false, collection:notnull")] - public static bool IsNullOrEmpty([NotNullWhen(false)] this IEnumerable? collection) => collection is null || collection.Count() == 0; + [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")] + public static bool InheritsFrom( +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClassOrInterfaceType) => baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType) : type.DerivesFrom(baseClassOrInterfaceType); /// - /// Checks if the specified string is null or empty. + /// Checks if the given type derives from the specified base class or interface type. This overload uses the specified + /// to compare the types. /// - /// The string to be checked. + /// The type to be checked. + /// The type describing an interface or base class that should derive from or implement. + /// The equality comparer used to compare the types. + /// Thrown when , or , or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> false, string:notnull; => true, string:canbenull")] - public static bool IsNullOrEmpty([NotNullWhen(false)] this string? @string) => string.IsNullOrEmpty(@string); + [ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")] + public static bool InheritsFrom( +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClassOrInterfaceType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType)).IsInterface ? type.Implements(baseClassOrInterfaceType, typeComparer) : type.DerivesFrom(baseClassOrInterfaceType, typeComparer); /// - /// Ensures that the specified URI has the "https" scheme, or otherwise throws an . + /// Ensures that the specified URI is an absolute one, or otherwise throws a . /// /// The URI to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when uses a different scheme than "https". - /// Thrown when is relative and thus has no scheme. + /// Thrown when is not an absolute URI. /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustHaveScheme("https", parameterName, message); + public static Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.MustNotBeNull(parameterName, message).IsAbsoluteUri == false) + { + Throw.MustBeAbsoluteUri(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Ensures that the specified URI has the "https" scheme, or otherwise throws your custom exception. + /// Ensures that the specified URI is an absolute one, or otherwise throws your custom exception. /// /// The URI to be checked. /// The delegate that creates the exception to be thrown. is passed to this delegate. - /// Your custom exception thrown when uses a different scheme than "https", or when is a relative URI, or when is null. + /// Your custom exception thrown when is not an absolute URI, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static Uri MustBeHttpsUrl([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) => parameter.MustHaveScheme("https", exceptionFactory); - /// - /// Ensures that the string is not null and trimmed at the end, or otherwise throws a . - /// Empty strings are regarded as trimmed. - /// - /// The string to be checked. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not trimmed at the end, i.e. they end with white space characters. - /// Empty strings are regarded as trimmed. - /// - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - if (!parameter.MustNotBeNull(parameterName, message).IsTrimmedAtEnd()) - { - Throw.NotTrimmedAtEnd(parameter, parameterName, message); - } - - return parameter; - } - - /// - /// Ensures that the string is not null and trimmed at the end, or otherwise throws your custom exception. - /// Empty strings are regarded as trimmed. - /// - /// The string to be checked. - /// The delegate that creates your custom exception. is passed to this delegate. - /// Your custom exception thrown when is null or not trimmed at the end. Empty strings are regarded as trimmed. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) + public static Uri MustBeAbsoluteUri([NotNull][ValidatedNotNull] this Uri? parameter, Func exceptionFactory) { - if (parameter is null || !parameter.AsSpan().IsTrimmedAtEnd()) + if (parameter is null || parameter.IsAbsoluteUri == false) { Throw.CustomException(exceptionFactory, parameter); } @@ -4643,1044 +4663,1054 @@ public static string MustBeTrimmedAtEnd([NotNull][ValidatedNotNull] this string? } /// - /// Ensures that the has at least the specified length, or otherwise throws an . + /// Ensures that the collection is not null or empty, or otherwise throws an . /// - /// The to be checked. - /// The minimum length the should have. + /// The collection to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has less than the specified length. - /// The default instance of will be treated as having length 0. + /// Thrown when has no items. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveMinimumLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this TCollection? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - var parameterLength = parameter.IsDefault ? 0 : parameter.Length; - if (parameterLength < length) + if (parameter.Count(parameterName, message) == 0) { - Throw.InvalidMinimumImmutableArrayLength(parameter, length, parameterName, message); + Throw.EmptyCollection(parameterName, message); } return parameter; } /// - /// Ensures that the has at least the specified length, or otherwise throws your custom exception. + /// Ensures that the collection is not null or empty, or otherwise throws your custom exception. /// - /// The to be checked. - /// The minimum length the should have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when has less than the specified length. - /// The default instance of will be treated as having length 0. + /// The collection to be checked. + /// The delegate that creates your custom exception. + /// Thrown when has no items, or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveMinimumLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this TCollection? parameter, Func exceptionFactory) + where TCollection : class, IEnumerable { - var parameterLength = parameter.IsDefault ? 0 : parameter.Length; - if (parameterLength < length) + if (parameter is null || parameter.Count() == 0) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified parameter is not null when is a reference type, or otherwise - /// throws an . PLEASE NOTICE: you should only use this assertion in generic contexts, - /// use by default. + /// Ensures that the specified string is not null or empty, or otherwise throws an or . /// - /// The value to be checked for null. + /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when is a reference type and is null. + /// Thrown when is an empty string. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static string MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (default(T) != null) + if (parameter is null) { - // If we end up here, parameter cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - - return parameter; -#pragma warning restore CS8777 + Throw.ArgumentNull(parameterName, message); } - if (parameter is null) + if (parameter.Length == 0) { - Throw.ArgumentNull(parameterName, message); + Throw.EmptyString(parameterName, message); } return parameter; } /// - /// Ensures that the specified parameter is not null when is a reference type, or otherwise - /// throws your custom exception. PLEASE NOTICE: you should only use this assertion in generic contexts, - /// use by default. + /// Ensures that the specified string is not null or empty, or otherwise throws your custom exception. /// - /// The value to be checked for null. - /// The delegate that creates your custom exception. - /// Your custom exception thrown when is a reference type and is null. + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is an empty string or null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeNullReference([NotNull, ValidatedNotNull, NoEnumeration] this T parameter, Func exceptionFactory) + public static string MustNotBeNullOrEmpty([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (default(T) != null) - { - // If we end up here, parameter cannot be null -#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. - - return parameter; -#pragma warning restore CS8777 - } - - if (parameter is null) + if (parameter.IsNullOrEmpty()) { - Throw.CustomException(exceptionFactory); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the string does not end with the specified value, or otherwise throws a . + /// Checks if the two specified types are equivalent. This is true when both types are equal or + /// when one type is a constructed generic type and the other type is the corresponding generic type definition. /// - /// The string to be checked. - /// The other string must not end with. - /// One of the enumeration values that specifies the rules for the search (optional). The default value is . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when ends with . - /// Thrown when or is null. - /// Thrown when is not a valid value. - public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// The first type to be checked. + /// The other type to be checked. + /// + /// True if both types are null, or if both are equal, or if one type + /// is a constructed generic type and the other one is the corresponding generic type definition, else false. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsEquivalentTypeTo(this Type? type, Type? other) => ReferenceEquals(type, other) || (type is not null && other is not null && (type == other || (type.IsConstructedGenericType != other.IsConstructedGenericType && CheckTypeEquivalency(type, other)))); + private static bool CheckTypeEquivalency(Type type, Type other) { - if (parameter.MustNotBeNull(parameterName, message).EndsWith(value, comparisonType)) + if (type.IsConstructedGenericType) { - Throw.StringEndsWith(parameter, value, comparisonType, parameterName, message); + return type.GetGenericTypeDefinition() == other; } - return parameter; + return other.GetGenericTypeDefinition() == type; } /// - /// Ensures that the string does not end with the specified value, or otherwise throws your custom exception. + /// Checks if the given is equal to the specified or if it implements it. Internally, this + /// method uses so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The string to be checked. - /// The other string must not end with. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// - /// Your custom exception thrown when ends with , - /// or when is null, - /// or when is null. - /// + /// The type to be checked. + /// The type that is equivalent to or the interface type that implements. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements( +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType); + /// + /// Checks if the given is equal to the specified or if it implements it. This overload uses the specified + /// to compare the types. + /// + /// , + /// The type to be checked. + /// The type that is equivalent to or the interface type that implements. + /// The equality comparer used to compare the interface types. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrImplements( +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer); + /// + /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when the specified is less than . + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.EndsWith(value)) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.MustNotBeLessThan(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string does not end with the specified value, or otherwise throws your custom exception. + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The other string must not end with. - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates your custom exception. , , and are passed to this delegate. - /// - /// Your custom exception thrown when ends with , - /// or when is null, - /// or when is null. - /// + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] - public static string MustNotEndWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off - if (parameter is null || value is null || parameter.EndsWith(value, comparisonType)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) < 0) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomException(exceptionFactory, parameter!, other); } return parameter; } /// - /// Ensures that the string has the specified length, or otherwise throws a . + /// Ensures that the specified is not less than the given value, or otherwise throws an . /// - /// The string to be checked. - /// The asserted length of the string. + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when has a length other than . + /// Thrown when the specified is less than . /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static T MustBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable { - if (parameter.MustNotBeNull(parameterName, message).Length != length) + if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) { - Throw.StringLengthNotEqualTo(parameter, length, parameterName, message); + Throw.MustBeGreaterThanOrEqualTo(parameter, other, parameterName, message); } return parameter; } /// - /// Ensures that the string has the specified length, or otherwise throws your custom exception. + /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The asserted length of the string. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when is null or when it has a length other than . + /// The comparable to be checked. + /// The boundary value that must be less than or equal to . + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when the specified is less than , or when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustHaveLength([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustBeGreaterThanOrEqualTo([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) + where T : IComparable { - if (parameter is null || parameter.Length != length) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (parameter is null || parameter.CompareTo(other) < 0) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter!, other); } return parameter; } /// - /// Ensures that the span has the specified length, or otherwise throws an . + /// Checks if the type implements the specified interface type. Internally, this method uses + /// so that constructed generic types and their corresponding generic type definitions are regarded as equal. /// - /// The span to be checked. - /// The length that the span must have. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified length. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustHaveLength(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + /// The type to be checked. + /// The interface type that should implement. + /// Thrown when or is null. + [ContractAnnotation("type:null => halt; interfaceType:null => halt")] + public static bool Implements( +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type interfaceType) { - ((ReadOnlySpan)parameter).MustHaveLength(length, parameterName, message); - return parameter; + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) + { + if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i])) + { + return true; + } + } + + return false; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Checks if the type implements the specified interface type. This overload uses the specified + /// to compare the interface types. /// - /// The span to be checked. - /// The length that the span must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span MustHaveLength(this Span parameter, int length, SpanExceptionFactory exceptionFactory) + /// The type to be checked. + /// The interface type that should implement. + /// The equality comparer used to compare the interface types. + /// Thrown when , or , or is null. + [ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")] + public static bool Implements( +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type interfaceType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) { - if (parameter.Length != length) + type.MustNotBeNull(); + interfaceType.MustNotBeNull(); + typeComparer.MustNotBeNull(); + var implementedInterfaces = type.GetInterfaces(); + for (var i = 0; i < implementedInterfaces.Length; ++i) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + if (typeComparer.Equals(implementedInterfaces[i], interfaceType)) + { + return true; + } } - return parameter; + return false; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws an , an , or a . /// - /// The span to be checked. - /// The length that the span must have. + /// The string to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Your custom exception thrown when does not have the specified length. + /// Thrown when contains only white space. + /// Thrown when is an empty string. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustNotBeNullOrWhiteSpace([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.Length != length) + parameter.MustNotBeNullOrEmpty(parameterName, message); + foreach (var character in parameter) { - Throw.InvalidSpanLength(parameter, length, parameterName, message); + if (!character.IsWhiteSpace()) + { + return parameter; + } } - return parameter; + Throw.WhiteSpaceString(parameter, parameterName, message); + return null; } /// - /// Ensures that the span has the specified length, or otherwise throws your custom exception. + /// Ensures that the specified string is not null, empty, or contains only white space, or otherwise throws your custom exception. /// - /// The span to be checked. - /// The length that the span must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. + /// The string to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null, empty, or contains only white space. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ReadOnlySpan MustHaveLength(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory: null => halt")] + public static string MustNotBeNullOrWhiteSpace([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (parameter.Length != length) + if (parameter.IsNullOrWhiteSpace()) { - Throw.CustomSpanException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the immutable array has the specified length, or otherwise throws an . + /// Ensures that the specified uses , or otherwise throws an . /// - /// The immutable array to be checked. - /// The length that the immutable array must have. + /// The date time to be checked. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not have the specified length. + /// Thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveLength(this ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static DateTime MustBeUtc(this DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - var actualLength = parameter.IsDefault ? 0 : parameter.Length; - if (actualLength != length) + if (parameter.Kind != DateTimeKind.Utc) { - Throw.InvalidImmutableArrayLength(parameter, length, parameterName, message); + Throw.MustBeUtcDateTime(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the immutable array has the specified length, or otherwise throws your custom exception. + /// Ensures that the specified uses , or otherwise throws your custom exception. /// - /// The immutable array to be checked. - /// The length that the immutable array must have. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not have the specified length. + /// The date time to be checked. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when does not use . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustHaveLength(this ImmutableArray parameter, int length, Func, int, Exception> exceptionFactory) + [ContractAnnotation("exceptionFactory:null => halt")] + public static DateTime MustBeUtc(this DateTime parameter, Func exceptionFactory) { - var actualLength = parameter.IsDefault ? 0 : parameter.Length; - if (actualLength != length) + if (parameter.Kind != DateTimeKind.Utc) { - Throw.CustomException(exceptionFactory, parameter, length); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Checks if the specified type derives from the other type. Internally, this method uses - /// by default so that constructed generic types and their corresponding generic type definitions are regarded as equal. + /// Checks if the specified character is a digit. /// - /// The type info to be checked. - /// The base class that should derive from. - /// Thrown when or is null. - [ContractAnnotation("type:null => halt; baseClass:null => halt")] - public static bool DerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClass) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsDigit(this char character) => char.IsDigit(character); + /// + /// Checks if the specified string is trimmed at the start, i.e. it does not start with + /// white space characters. Inputting an empty string will return true. + /// + /// The string to be checked. + /// + /// The value indicating whether true or false should be returned from this method when the + /// is null. The default value is true. + /// + /// + /// True if the is trimmed at the start, else false. + /// An empty string will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtStart(this string? parameter, bool regardNullAsTrimmed = true) => parameter is null ? regardNullAsTrimmed : parameter.AsSpan().IsTrimmedAtStart(); + /// + /// Checks if the specified character span is trimmed at the start, i.e. it does not start with + /// white space characters. Inputting an empty span will return true. + /// + /// The character span to be checked. + /// + /// True if the is trimmed at the start, else false. + /// An empty span will result in true. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsTrimmedAtStart(this ReadOnlySpan parameter) => parameter.Length == 0 || !parameter[0].IsWhiteSpace(); + /// + /// Ensures that the collection has at least the specified number of items, or otherwise throws an . + /// + /// The collection to be checked. + /// The number of items the collection should have at least. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when does not contain at least the specified number of items. + /// Thrown when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMinimumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where TCollection : class, IEnumerable { - baseClass.MustNotBeNull(nameof(baseClass)); - var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; - while (currentBaseType != null) + if (parameter.Count(parameterName, message) < count) { - if (currentBaseType.IsEquivalentTypeTo(baseClass)) - { - return true; - } - - currentBaseType = currentBaseType.BaseType; + Throw.InvalidMinimumCollectionCount(parameter, count, parameterName, message); } - return false; + return parameter; } /// - /// Checks if the specified type derives from the other type. This overload uses the specified - /// to compare the types. + /// Ensures that the collection has at least the specified number of items, or otherwise throws your custom exception. /// - /// The type info to be checked. - /// The base class that should derive from. - /// The equality comparer used to compare the types. - /// Thrown when , or , or is null. - [ContractAnnotation("type:null => halt; baseClass:null => halt; typeComparer:null => halt")] - public static bool DerivesFrom([NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type baseClass, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) + /// The collection to be checked. + /// The number of items the collection should have at least. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when does not contain at least the specified number of items, or when is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static TCollection MustHaveMinimumCount([NotNull][ValidatedNotNull] this TCollection? parameter, int count, Func exceptionFactory) + where TCollection : class, IEnumerable { - baseClass.MustNotBeNull(nameof(baseClass)); - typeComparer.MustNotBeNull(nameof(typeComparer)); - var currentBaseType = type.MustNotBeNull(nameof(type)).BaseType; - while (currentBaseType != null) + if (parameter is null || parameter.Count() < count) { - if (typeComparer.Equals(currentBaseType, baseClass)) - { - return true; - } - - currentBaseType = currentBaseType.BaseType; + Throw.CustomException(exceptionFactory, parameter, count); } - return false; + return parameter; } /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the string is a valid email address using the default email regular expression + /// defined in , or otherwise throws an . /// - /// The value to be checked. - /// The value that should be greater than or approximately equal to. + /// The email address that will be validated. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not greater than or approximately equal to . - /// + /// Thrown when is no valid email address. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeGreaterThanOrApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeGreaterThanOrApproximately(other, 0.0001, parameterName, message); + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress()) + { + Throw.InvalidEmailAddress(parameter, parameterName, message); + } + + return parameter; + } + /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the string is a valid email address using the default email regular expression + /// defined in , or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should be greater than or approximately equal to. - /// - /// The delegate that creates your custom exception. and - /// are passed to this delegate. - /// - /// - /// Thrown when is not greater than or approximately equal to . - /// + /// The email address that will be validated. + /// The delegate that creates your custom exception. is passed to this delegate. + /// Your custom exception thrown when is null or no valid email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeGreaterThanOrApproximately(this double parameter, double other, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Func exceptionFactory) { - if (!parameter.IsGreaterThanOrApproximately(other)) + if (!parameter.IsEmailAddress()) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomException(exceptionFactory, parameter); } return parameter; } /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is a valid email address using the provided regular expression, + /// or otherwise throws an . /// - /// The value to be checked. - /// The value that should be greater than or approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. + /// The email address that will be validated. + /// The regular expression that determines if the input string is a valid email. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not greater than or approximately equal to . - /// + /// Thrown when is no valid email address. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + if (!parameter.MustNotBeNull(parameterName, message).IsEmailAddress(emailAddressPattern)) { - Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); + Throw.InvalidEmailAddress(parameter, parameterName, message); } return parameter; } /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the string is a valid email address using the provided regular expression, + /// or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should be greater than or approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// The delegate that creates your custom exception. , - /// , and are passed to this delegate. - /// - /// - /// Your custom exception thrown when is not greater than or approximately equal to . - /// + /// The email address that will be validated. + /// The regular expression that determines if the input string is a valid email. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or no valid email address. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeGreaterThanOrApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; emailAddressPattern:null => halt")] + public static string MustBeEmailAddress([NotNull][ValidatedNotNull] this string? parameter, Regex emailAddressPattern, Func exceptionFactory) { - if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off + if (emailAddressPattern is null || !parameter.IsEmailAddress(emailAddressPattern)) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter, emailAddressPattern!); } return parameter; } /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, using the default tolerance of 0.0001f, or otherwise throws an - /// . + /// Checks if the given is equal to the specified or if it derives from it or implements it. + /// Internally, this method uses so that constructed generic types and their corresponding generic type definitions + /// are regarded as equal. /// - /// The value to be checked. - /// The value that should be greater than or approximately equal to. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not greater than or approximately equal to . - /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeGreaterThanOrApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeGreaterThanOrApproximately(other, 0.0001f, parameterName, message); + [ContractAnnotation("type:null => halt; otherType:null => halt")] + public static bool IsOrInheritsFrom( +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType) => type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType); /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Checks if the given is equal to the specified or if it derives from it or implements it. + /// This overload uses the specified to compare the types. /// - /// The value to be checked. - /// The value that should be greater than or approximately equal to. - /// - /// The delegate that creates your custom exception. and - /// are passed to this delegate. - /// - /// - /// Thrown when is not greater than or approximately equal to . - /// + /// The type to be checked. + /// The type that is equivalent to or the base class type where derives from. + /// The equality comparer used to compare the types. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeGreaterThanOrApproximately(this float parameter, float other, Func exceptionFactory) - { - if (!parameter.IsGreaterThanOrApproximately(other)) - { - Throw.CustomException(exceptionFactory, parameter, other); - } - - return parameter; - } - + [ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")] + public static bool IsOrInheritsFrom( +#if NET8_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] +#endif + [NotNull][ValidatedNotNull] this Type type, [NotNull][ValidatedNotNull] Type otherType, [NotNull][ValidatedNotNull] IEqualityComparer typeComparer) => typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer); /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is shorter than the specified length, or otherwise throws a . /// - /// The value to be checked. - /// The value that should be greater than or approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. + /// The string to be checked. + /// The length that the string must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not greater than or approximately equal to . - /// + /// Thrown when has a length greater than or equal to . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeGreaterThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + if (parameter.MustNotBeNull(parameterName, message).Length >= length) { - Throw.MustBeGreaterThanOrApproximately(parameter, other, tolerance, parameterName, message); + Throw.StringNotShorterThan(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the specified is greater than or approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the string is shorter than the specified length, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should be greater than or approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// The delegate that creates your custom exception. , - /// , and are passed to this delegate. - /// - /// - /// Your custom exception thrown when is not greater than or approximately equal to . - /// + /// The string to be checked. + /// The length that the string must be shorter than. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length greater than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeGreaterThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeShorterThan([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (!parameter.IsGreaterThanOrApproximately(other, tolerance)) + if (parameter is null || parameter.Length >= length) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified is greater than the given value, or otherwise throws an . + /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// - /// The comparable to be checked. - /// The boundary value that must be less than . + /// The span to be checked. + /// The length value that the span must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than or equal to . - /// Thrown when is null. + /// Thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustBeGreaterThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + public static Span MustBeShorterThan(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) <= 0) - { - Throw.MustBeGreaterThan(parameter, other, parameterName, message); - } - + ((ReadOnlySpan)parameter).MustBeShorterThan(length, parameterName, message); return parameter; } /// - /// Ensures that the specified is greater than the given value, or otherwise throws your custom exception. + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than or equal to , or when is null. + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustBeGreaterThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + public static Span MustBeShorterThan(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) <= 0) + if (parameter.Length >= length) { - Throw.CustomException(exceptionFactory, parameter!, other); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the span is shorter than the specified length, or otherwise throws an . /// - /// The value to be checked. - /// The value that should be less than or approximately equal to. + /// The span to be checked. + /// The length value that the span must be shorter than. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not less than or approximately equal to . - /// + /// Thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeLessThanOrApproximately(this double parameter, double other, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) => parameter.MustBeLessThanOrApproximately(other, 0.0001, parameterName, message); + public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + if (parameter.Length >= length) + { + Throw.SpanMustBeShorterThan(parameter, length, parameterName, message); + } + + return parameter; + } + /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . + /// Ensures that the span is shorter than the specified length, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should be less than or approximately equal to. - /// - /// The delegate that creates your custom exception. and - /// are passed to this delegate. - /// - /// - /// Thrown when is not less than or approximately equal to . - /// + /// The span to be checked. + /// The length value that the span must be shorter than. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is longer than or equal to . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeLessThanOrApproximately(this double parameter, double other, Func exceptionFactory) + public static ReadOnlySpan MustBeShorterThan(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (!parameter.IsLessThanOrApproximately(other)) + if (parameter.Length >= length) { - Throw.CustomException(exceptionFactory, parameter, other); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the string is longer than or equal to the specified length, or otherwise throws a . /// - /// The value to be checked. - /// The value that should be less than or approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. + /// The string to be checked. + /// The length that the string must be longer than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not less than or approximately equal to . - /// + /// Thrown when has a length shorter than . + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsLessThanOrApproximately(other, tolerance)) + if (parameter.MustNotBeNull(parameterName, message).Length < length) { - Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + Throw.StringNotLongerThanOrEqualTo(parameter, length, parameterName, message); } return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the string is longer than or equal to the specified length, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should be less than or approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// The delegate that creates your custom exception. , - /// , and are passed to this delegate. - /// - /// - /// Your custom exception thrown when is not less than or approximately equal to . - /// + /// The string to be checked. + /// The length that the string must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// Your custom exception thrown when is null or when it has a length shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static double MustBeLessThanOrApproximately(this double parameter, double other, double tolerance, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static string MustBeLongerThanOrEqualTo([NotNull][ValidatedNotNull] this string? parameter, int length, Func exceptionFactory) { - if (!parameter.IsLessThanOrApproximately(other, tolerance)) + if (parameter is null || parameter.Length < length) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.CustomException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, using the default tolerance of 0.0001f, or otherwise throws an - /// . + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . /// - /// The value to be checked. - /// The value that should be less than or approximately equal to. + /// The span to be checked. + /// The value that the span must be longer than or equal to. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not less than or approximately equal to . - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeLessThanOrApproximately(this float parameter, float other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => parameter.MustBeLessThanOrApproximately(other, 0.0001f, parameterName, message); - /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, using the default tolerance of 0.0001, or otherwise throws an - /// . - /// - /// The value to be checked. - /// The value that should be less than or approximately equal to. - /// - /// The delegate that creates your custom exception. and - /// are passed to this delegate. - /// - /// - /// Thrown when is not less than or approximately equal to . - /// + /// Thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeLessThanOrApproximately(this float parameter, float other, Func exceptionFactory) + public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.IsLessThanOrApproximately(other)) - { - Throw.CustomException(exceptionFactory, parameter, other); - } - + ((ReadOnlySpan)parameter).MustBeLongerThanOrEqualTo(length, parameterName, message); return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, or otherwise throws an . + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. /// - /// The value to be checked. - /// The value that should be less than or approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// - /// Thrown when is not less than or approximately equal to . - /// + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static Span MustBeLongerThanOrEqualTo(this Span parameter, int length, SpanExceptionFactory exceptionFactory) { - if (!parameter.IsLessThanOrApproximately(other, tolerance)) + if (parameter.Length < length) { - Throw.MustBeLessThanOrApproximately(parameter, other, tolerance, parameterName, message); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the specified is less than or approximately equal to the given - /// value, or otherwise throws your custom exception. + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws an . /// - /// The value to be checked. - /// The value that should be less than or approximately equal to. - /// The tolerance indicating how much the two values may differ from each other. - /// - /// The delegate that creates your custom exception. , - /// , and are passed to this delegate. - /// - /// - /// Your custom exception thrown when is not less than or approximately equal to . - /// + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static float MustBeLessThanOrApproximately(this float parameter, float other, float tolerance, Func exceptionFactory) + public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (!parameter.IsLessThanOrApproximately(other, tolerance)) + if (parameter.Length < length) { - Throw.CustomException(exceptionFactory, parameter, other, tolerance); + Throw.SpanMustBeLongerThanOrEqualTo(parameter, length, parameterName, message); } return parameter; } /// - /// Checks if the value is not within the specified range. - /// - /// The comparable to be checked. - /// The range where must not be in-between. - /// True if the parameter is not within the specified range, else false. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool IsNotIn([NotNull][ValidatedNotNull] this T parameter, Range range) - where T : IComparable => !range.IsValueWithinRange(parameter); - /// - /// Ensures that the collection contains the specified item, or otherwise throws a . + /// Ensures that the span is longer than or equal to the specified length, or otherwise throws your custom exception. /// - /// The collection to be checked. - /// The item that must be part of the collection. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when is null. + /// The span to be checked. + /// The value that the span must be longer than or equal to. + /// The delegate that creates your custom exception. and are passed to it. + /// Your custom exception thrown when is shorter than . [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where TCollection : class, IEnumerable + public static ReadOnlySpan MustBeLongerThanOrEqualTo(this ReadOnlySpan parameter, int length, ReadOnlySpanExceptionFactory exceptionFactory) { - if (parameter is ICollection collection) - { - if (!collection.Contains(item)) - { - Throw.MissingItem(parameter, item, parameterName, message); - } - - return parameter; - } - - if (!parameter.MustNotBeNull(parameterName, message).Contains(item)) + if (parameter.Length < length) { - Throw.MissingItem(parameter, item, parameterName, message); + Throw.CustomSpanException(exceptionFactory, parameter, length); } return parameter; } /// - /// Ensures that the collection contains the specified item, or otherwise throws your custom exception. + /// Ensures that the string does not start with the specified value, or otherwise throws a . /// - /// The collection to be checked. - /// The item that must be part of the collection. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static TCollection MustContain([NotNull][ValidatedNotNull] this TCollection? parameter, TItem item, Func exceptionFactory) - where TCollection : class, IEnumerable + /// The string to be checked. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search (optional). The default value is . + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when starts with . + /// Thrown when or is null. + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType = StringComparison.CurrentCulture, [CallerArgumentExpression(nameof(parameter))] string? parameterName = null, string? message = null) { - if (parameter is ICollection collection) - { - if (!collection.Contains(item)) - { - Throw.CustomException(exceptionFactory, parameter, item); - } - - return parameter; - } - - if (parameter is null || !parameter.Contains(item)) + if (parameter.MustNotBeNull(parameterName, message).StartsWith(value, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter, item); + Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); } return parameter; } /// - /// Ensures that the string contains the specified substring, or otherwise throws a . + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. /// /// The string to be checked. - /// The substring that must be part of . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . - /// Thrown when or is null. + /// The other string that must not start with. + /// The delegate that creates your custom exception. and are passed to this delegate. + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string? value, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, [NotNull, ValidatedNotNull] Func exceptionFactory) { - if (!parameter.MustNotBeNull(parameterName, message).Contains(value.MustNotBeNull(nameof(value), message))) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value)) { - Throw.StringDoesNotContain(parameter, value, parameterName, message); + Throw.CustomException(exceptionFactory, parameter, value!); } return parameter; } /// - /// Ensures that the string contains the specified value, or otherwise throws your custom exception. + /// Ensures that the string does not start with the specified value, or otherwise throws your custom exception. /// /// The string to be checked. - /// The substring that must be part of . - /// The delegate that creates you custom exception. and are passed to this delegate. + /// The other string that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// The delegate that creates your custom exception. , , and are passed to this delegate. /// - /// Your custom exception thrown when does not contain , + /// Your custom exception thrown when does not start with , /// or when is null, /// or when is null. /// + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, Func exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; value:null => halt; exceptionFactory:null => halt")] + public static string MustNotStartWith([NotNull, ValidatedNotNull] this string? parameter, [NotNull, ValidatedNotNull] string value, StringComparison comparisonType, [NotNull, ValidatedNotNull] Func exceptionFactory) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || !parameter.Contains(value)) + // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract -- caller might have NRTs turned off + if (parameter is null || value is null || parameter.StartsWith(value, comparisonType)) { - Throw.CustomException(exceptionFactory, parameter, value!); + Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); } return parameter; } /// - /// Ensures that the string contains the specified value, or otherwise throws a . + /// Ensures that the span does not start with the specified value, or otherwise throws a . /// - /// The string to be checked. - /// The substring that must be part of . + /// The span to be checked. + /// The other span that must not start with. /// One of the enumeration values that specifies the rules for the search. /// The name of the parameter (optional). /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . + /// Thrown when starts with . /// Thrown when or is null. - /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (parameter.MustNotBeNull(parameterName, message).IndexOf(value.MustNotBeNull(nameof(value), message), comparisonType) < 0) + if (parameter.StartsWith(value, comparisonType)) { - Throw.StringDoesNotContain(parameter, value, comparisonType, parameterName, message); + Throw.StringStartsWith(parameter, value, comparisonType, parameterName, message); } return parameter; } /// - /// Ensures that the string contains the specified value, or otherwise throws your custom exception. + /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. /// - /// The string to be checked. - /// The substring that must be part of . - /// One of the enumeration values that specifies the rules for the search. - /// The delegate that creates you custom exception. , , and are passed to this delegate. + /// The span to be checked. + /// The other span that must not start with. + /// + /// The delegate that creates your custom exception. and + /// are passed to this delegate. + /// /// - /// Your custom exception thrown when does not contain , + /// Your custom exception thrown when does not start with , /// or when is null, /// or when is null. /// - /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static string MustContain([NotNull][ValidatedNotNull] this string? parameter, string value, StringComparison comparisonType, Func exceptionFactory) + public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, ReadOnlySpansExceptionFactory exceptionFactory) + where T : IEquatable { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || value is null || parameter.IndexOf(value, comparisonType) < 0) + if (parameter.StartsWith(value)) { - Throw.CustomException(exceptionFactory, parameter, value!, comparisonType); + Throw.CustomSpanException(exceptionFactory, parameter, value); } return parameter; } /// - /// Ensures that the immutable array contains the specified item, or otherwise throws a . + /// Ensures that the span does not start with the specified value, or otherwise throws your custom exception. /// - /// The immutable array to be checked. - /// The item that must be part of the immutable array. - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when does not contain . + /// The span to be checked. + /// The other span that must not start with. + /// One of the enumeration values that specifies the rules for the search. + /// + /// The delegate that creates your custom exception. , + /// , and are passed to this delegate. + /// + /// + /// Your custom exception thrown when does not start with , + /// or when is null, + /// or when is null. + /// + /// + /// Thrown when is not a valid value. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustContain(this ImmutableArray parameter, T item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + public static ReadOnlySpan MustNotStartWith(this ReadOnlySpan parameter, ReadOnlySpan value, StringComparison comparisonType, ReadOnlySpansExceptionFactory exceptionFactory) { - if (!parameter.Contains(item)) + if (parameter.StartsWith(value, comparisonType)) { - Throw.MissingItem(parameter, item, parameterName, message); + Throw.CustomSpanException(exceptionFactory, parameter, value, comparisonType); } return parameter; } /// - /// Ensures that the immutable array contains the specified item, or otherwise throws your custom exception. + /// Ensures that the specified parameter is not the default value, or otherwise throws an + /// for reference types, or an for value types. /// - /// The immutable array to be checked. - /// The item that must be part of the immutable array. - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when does not contain . + /// The value to be checked. + /// The name of the parameter (optional). + /// The message that will be passed to the resulting exception (optional). + /// Thrown when is a reference type and null. + /// Thrown when is a value type and the default value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static ImmutableArray MustContain(this ImmutableArray parameter, T item, Func, T, Exception> exceptionFactory) + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] + public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) { - if (!parameter.Contains(item)) + if (default(T)is null) { - Throw.CustomException(exceptionFactory, parameter, item); + if (parameter is null) + { + Throw.ArgumentNull(parameterName, message); + } + + return parameter; + } + + if (EqualityComparer.Default.Equals(parameter, default !)) + { + Throw.ArgumentDefault(parameterName, message); } +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 } /// - /// Ensures that the specified is not less than the given value, or otherwise throws an . + /// Ensures that the specified parameter is not the default value, or otherwise throws your custom exception. /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The name of the parameter (optional). - /// The message that will be passed to the resulting exception (optional). - /// Thrown when the specified is less than . - /// Thrown when is null. + /// The value to be checked. + /// The delegate that creates your custom exception. + /// Your custom exception thrown when is the default value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull")] - public static T MustNotBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable + [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] + public static T MustNotBeDefault([NotNull, ValidatedNotNull] this T parameter, Func exceptionFactory) { - if (parameter.MustNotBeNullReference(parameterName, message).CompareTo(other) < 0) + if (default(T)is null) { - Throw.MustNotBeLessThan(parameter, other, parameterName, message); + if (parameter is null) + { + Throw.CustomException(exceptionFactory); + } + + return parameter; + } + + if (EqualityComparer.Default.Equals(parameter, default !)) + { + Throw.CustomException(exceptionFactory); } +#pragma warning disable CS8777 // Parameter must have a non-null value when exiting. + return parameter; +#pragma warning restore CS8777 } + } + /// + /// Represents an that compares strings using the + /// ordinal sort rules, ignoring the case and the white space characters. + /// + internal sealed class OrdinalIgnoreCaseIgnoreWhiteSpaceComparer : IEqualityComparer + { /// - /// Ensures that the specified is not less than the given value, or otherwise throws your custom exception. + /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the case and + /// the white space of the provided strings. /// - /// The comparable to be checked. - /// The boundary value that must be less than or equal to . - /// The delegate that creates your custom exception. and are passed to this delegate. - /// Your custom exception thrown when the specified is less than , or when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("parameter:null => halt; parameter:notnull => notnull; exceptionFactory:null => halt")] - public static T MustNotBeLessThan([NotNull][ValidatedNotNull] this T parameter, T other, Func exceptionFactory) - where T : IComparable + /// Thrown when or are null. + public bool Equals(string? x, string? y) { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - caller might have NRTs turned off - if (parameter is null || parameter.CompareTo(other) < 0) + x.MustNotBeNull(nameof(x)); + y.MustNotBeNull(nameof(y)); + return x.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(y); + } + + /// + /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters + /// which are interpreted as case-insensitive. + /// + /// Thrown when is null. + public int GetHashCode(string @string) + { + @string.MustNotBeNull(nameof(@string)); + var hashBuilder = MultiplyAddHashBuilder.Create(); + foreach (var character in @string) { - Throw.CustomException(exceptionFactory, parameter!, other); + if (!character.IsWhiteSpace()) + { + hashBuilder.CombineIntoHash(char.ToLowerInvariant(character)); + } } - return parameter; + return hashBuilder.BuildHash(); } } /// - /// Represents an that uses - /// to compare types. This check works like the normal type equality comparison, but when two - /// generic types are compared, they are regarded as equal when one of them is a constructed generic type - /// and the other one is the corresponding generic type definition. + /// Provides regular expressions that are used in string assertions. /// - internal sealed class EquivalentTypeComparer : IEqualityComparer +#if NET8_0_OR_GREATER +public static partial class RegularExpressions +#else + internal static class RegularExpressions +#endif { /// - /// Gets a singleton instance of the equality comparer. - /// - public static readonly EquivalentTypeComparer Instance = new(); - /// - /// Checks if the two types are equivalent (using ). - /// This check works like the normal type equality comparison, but when two - /// generic types are compared, they are regarded as equal when one of them is a constructed generic type - /// and the other one is the corresponding generic type definition. + /// Gets the string that represents the . /// - /// The first type. - /// The second type. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool Equals(Type? x, Type? y) => x.IsEquivalentTypeTo(y); + // This is an AI-generated regex. I don't have any clue and I find it way to complex to ever understand it. + public const string EmailRegexText = @"^(?:(?:""(?:(?:[^""\\]|\\.)*)""|[\p{L}\p{N}!#$%&'*+\-/=?^_`{|}~-]+(?:\.[\p{L}\p{N}!#$%&'*+\-/=?^_`{|}~-]+)*)@(?:(?:[A-Za-z0-9](?:[A-Za-z0-9\-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,}|(?:\[(?:IPv6:[0-9A-Fa-f:.]+)\])|(?:25[0-5]|2[0-4]\d|[01]?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)){3}))$"; /// - /// Returns the hash code of the given type. When the specified type is a constructed generic type, - /// the hash code of the generic type definition is returned instead. + /// Gets the default regular expression for email validation. + /// This pattern is based on https://www.rhyous.com/2010/06/15/csharp-email-regular-expression/ and + /// was modified to satisfy all tests of https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/. /// - /// The type whose hash code is requested. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int GetHashCode(Type type) => // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - type is null ? 0 : type.IsConstructedGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode(); + public static readonly Regex EmailRegex = +#if NET8_0_OR_GREATER + GenerateEmailRegex(); + + [GeneratedRegex(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant)] + private static partial Regex GenerateEmailRegex(); +#else + new(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant | RegexOptions.Compiled); +#endif + } + + [AttributeUsage(AttributeTargets.Parameter)] + internal sealed class ValidatedNotNullAttribute : Attribute + { } /// @@ -5952,47 +5982,6 @@ public static Range ExclusiveBetween(T from, T to) public static Range For(ArraySegment segment) => new(0, segment.Count, true, false); } - /// - /// Provides regular expressions that are used in string assertions. - /// -#if NET8_0 -public static partial class RegularExpressions -#else - internal static class RegularExpressions -#endif - { - /// - /// Gets the string that represents the . - /// - // This is an AI-generated regex. I don't have any clue and I find it way to complex to ever understand it. - public const string EmailRegexText = @"^(?:(?:""(?:(?:[^""\\]|\\.)*)""|[\p{L}\p{N}!#$%&'*+\-/=?^_`{|}~-]+(?:\.[\p{L}\p{N}!#$%&'*+\-/=?^_`{|}~-]+)*)@(?:(?:[A-Za-z0-9](?:[A-Za-z0-9\-]*[A-Za-z0-9])?\.)+[A-Za-z]{2,}|(?:\[(?:IPv6:[0-9A-Fa-f:.]+)\])|(?:25[0-5]|2[0-4]\d|[01]?\d?\d)(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d?\d)){3}))$"; - /// - /// Gets the default regular expression for email validation. - /// This pattern is based on https://www.rhyous.com/2010/06/15/csharp-email-regular-expression/ and - /// was modified to satisfy all tests of https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/. - /// - public static readonly Regex EmailRegex = -#if NET8_0 - GenerateEmailRegex(); - - [GeneratedRegex(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant)] - private static partial Regex GenerateEmailRegex(); -#else - new(EmailRegexText, RegexOptions.ECMAScript | RegexOptions.CultureInvariant | RegexOptions.Compiled); -#endif - } - - /// - /// This class caches instances to avoid use of the typeof operator. - /// - internal abstract class Types - { - /// - /// Gets the type. - /// - public static readonly Type FlagsAttributeType = typeof(FlagsAttribute); - } - /// /// Specifies the culture, case , and sort rules when comparing strings. /// @@ -6044,44 +6033,86 @@ internal enum StringComparisonType } /// - /// Represents an that compares strings using the - /// ordinal sort rules, ignoring the case and the white space characters. + /// This class caches instances to avoid use of the typeof operator. /// - internal sealed class OrdinalIgnoreCaseIgnoreWhiteSpaceComparer : IEqualityComparer + internal abstract class Types { /// - /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the case and - /// the white space of the provided strings. + /// Gets the type. /// - /// Thrown when or are null. - public bool Equals(string? x, string? y) - { - x.MustNotBeNull(nameof(x)); + public static readonly Type FlagsAttributeType = typeof(FlagsAttribute); + } + + /// + /// Represents an that compares strings using the + /// ordinal sort rules and ignoring the white space characters. + /// + internal sealed class OrdinalIgnoreWhiteSpaceComparer : IEqualityComparer + { + /// + /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space + /// of the provided strings. + /// + /// Thrown when or are null. + public bool Equals(string? x, string? y) + { + x.MustNotBeNull(nameof(x)); y.MustNotBeNull(nameof(y)); - return x.EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(y); + return x.EqualsOrdinalIgnoreWhiteSpace(y); } /// - /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters - /// which are interpreted as case-insensitive. + /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters. /// /// Thrown when is null. public int GetHashCode(string @string) { @string.MustNotBeNull(nameof(@string)); - var hashBuilder = MultiplyAddHashBuilder.Create(); + var hashCodeBuilder = MultiplyAddHashBuilder.Create(); foreach (var character in @string) { if (!character.IsWhiteSpace()) { - hashBuilder.CombineIntoHash(char.ToLowerInvariant(character)); + hashCodeBuilder.CombineIntoHash(character); } } - return hashBuilder.BuildHash(); + return hashCodeBuilder.BuildHash(); } } + /// + /// Represents an that uses + /// to compare types. This check works like the normal type equality comparison, but when two + /// generic types are compared, they are regarded as equal when one of them is a constructed generic type + /// and the other one is the corresponding generic type definition. + /// + internal sealed class EquivalentTypeComparer : IEqualityComparer + { + /// + /// Gets a singleton instance of the equality comparer. + /// + public static readonly EquivalentTypeComparer Instance = new(); + /// + /// Checks if the two types are equivalent (using ). + /// This check works like the normal type equality comparison, but when two + /// generic types are compared, they are regarded as equal when one of them is a constructed generic type + /// and the other one is the corresponding generic type definition. + /// + /// The first type. + /// The second type. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Type? x, Type? y) => x.IsEquivalentTypeTo(y); + /// + /// Returns the hash code of the given type. When the specified type is a constructed generic type, + /// the hash code of the generic type definition is returned instead. + /// + /// The type whose hash code is requested. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int GetHashCode(Type type) => // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract + type is null ? 0 : type.IsConstructedGenericType ? type.GetGenericTypeDefinition().GetHashCode() : type.GetHashCode(); + } + /// /// Provides meta-information about enum values and the flag bitmask if the enum is marked with the . /// Can be used to validate that an enum value is valid. @@ -6108,7 +6139,7 @@ internal static class EnumInfo static EnumInfo() { -#if NET8_0 +#if NET8_0_OR_GREATER EnumConstantsArray = Enum.GetValues(); #else EnumConstantsArray = (T[])Enum.GetValues(typeof(T)); @@ -6176,577 +6207,534 @@ private static ulong ConvertToUInt64(T value) private static void ThrowUnknownEnumSize() => throw new InvalidOperationException($"The enum type \"{typeof(T)}\" has an unknown size of {EnumSize}. This means that the underlying enum type is not one of the supported ones."); } +} +namespace Light.GuardClauses.Exceptions +{ /// - /// Represents an that compares strings using the - /// ordinal sort rules and ignoring the white space characters. + /// This exception indicates that a collection has no items. /// - internal sealed class OrdinalIgnoreWhiteSpaceComparer : IEqualityComparer + [Serializable] + internal class EmptyCollectionException : InvalidCollectionCountException { /// - /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space - /// of the provided strings. + /// Creates a new instance of . /// - /// Thrown when or are null. - public bool Equals(string? x, string? y) + /// The name of the parameter (optional). + /// The message of the exception (optional). + public EmptyCollectionException(string? parameterName = null, string? message = null) : base(parameterName, message) { - x.MustNotBeNull(nameof(x)); - y.MustNotBeNull(nameof(y)); - return x.EqualsOrdinalIgnoreWhiteSpace(y); } - /// - /// Gets the hash code for the specified string. The hash code is created only from the non-white space characters. - /// - /// Thrown when is null. - public int GetHashCode(string @string) +#if !NET8_0_OR_GREATER + /// + protected EmptyCollectionException(SerializationInfo info, StreamingContext context) : base(info, context) { - @string.MustNotBeNull(nameof(@string)); - var hashCodeBuilder = MultiplyAddHashBuilder.Create(); - foreach (var character in @string) - { - if (!character.IsWhiteSpace()) - { - hashCodeBuilder.CombineIntoHash(character); - } - } - - return hashCodeBuilder.BuildHash(); } +#endif } - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class ValidatedNotNullAttribute : Attribute - { - } -} - -namespace Light.GuardClauses.Exceptions -{ /// - /// This exception indicates that a value is not defined in the corresponding enum type. + /// This exception indicates that an URI has an invalid scheme. /// [Serializable] - internal class EnumValueNotDefinedException : ArgumentException + internal class InvalidUriSchemeException : UriException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// - /// The name of the parameter. - /// The message of the exception. - public EnumValueNotDefinedException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The name of the parameter (optional). + /// The message of the exception (optional). + public InvalidUriSchemeException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected EnumValueNotDefinedException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidUriSchemeException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that configuration data is invalid. + /// This exception indicates that a value is invalid. /// [Serializable] - internal class InvalidConfigurationException : Exception + internal class InvalidDateTimeException : ArgumentException { /// - /// Initializes a new instance of . + /// Creates a new instance of . /// + /// The name of the parameter (optional). /// The message of the exception (optional). - /// The exception that is the cause of this one (optional). - public InvalidConfigurationException(string? message = null, Exception? innerException = null) : base(message, innerException) + public InvalidDateTimeException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected InvalidConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidDateTimeException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that an URI is relative instead of absolute. + /// This exception indicates that a string is in an invalid state. /// [Serializable] - internal class RelativeUriException : UriException + internal class StringException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public RelativeUriException(string? parameterName = null, string? message = null) : base(parameterName, message) + public StringException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected RelativeUriException(SerializationInfo info, StreamingContext context) : base(info, context) + protected StringException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a collection contains an item that must not be part of it. + /// This exception indicates that a string is empty. /// [Serializable] - internal class ExistingItemException : CollectionException + internal class EmptyStringException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ExistingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) + public EmptyStringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected ExistingItemException(SerializationInfo info, StreamingContext context) : base(info, context) + protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that an item is not present in a collection. + /// This exception indicates that two values are not equal. /// [Serializable] - internal class MissingItemException : CollectionException + internal class ValuesNotEqualException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public MissingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) + public ValuesNotEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected MissingItemException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ValuesNotEqualException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that an Email address is invalid. + /// This exception indicates that a value is not defined in the corresponding enum type. /// [Serializable] - internal class InvalidEmailAddressException : StringException + internal class EnumValueNotDefinedException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public InvalidEmailAddressException(string? parameterName = null, string? message = null) : base(parameterName, message) + /// The name of the parameter. + /// The message of the exception. + public EnumValueNotDefinedException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected InvalidEmailAddressException(SerializationInfo info, StreamingContext context) : base(info, context) + protected EnumValueNotDefinedException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that two values are equal. + /// This exception indicates that a collection contains an item that must not be part of it. /// [Serializable] - internal class ValuesEqualException : ArgumentException + internal class ExistingItemException : CollectionException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ValuesEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) + public ExistingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected ValuesEqualException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ExistingItemException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a string has an invalid length. + /// This exception indicates that an item is not present in a collection. /// [Serializable] - internal class StringLengthException : StringException + internal class MissingItemException : CollectionException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public StringLengthException(string? parameterName = null, string? message = null) : base(parameterName, message) + public MissingItemException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected StringLengthException(SerializationInfo info, StreamingContext context) : base(info, context) + protected MissingItemException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a string is in an invalid state. + /// This exception indicates that an URI is absolute instead of relative. /// [Serializable] - internal class SubstringException : StringException + internal class AbsoluteUriException : UriException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public SubstringException(string? parameterName = null, string? message = null) : base(parameterName, message) + public AbsoluteUriException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected SubstringException(SerializationInfo info, StreamingContext context) : base(info, context) + protected AbsoluteUriException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a collection has no items. + /// This exception indicates that that a GUID is empty. /// [Serializable] - internal class EmptyCollectionException : InvalidCollectionCountException + internal class EmptyGuidException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public EmptyCollectionException(string? parameterName = null, string? message = null) : base(parameterName, message) + public EmptyGuidException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected EmptyCollectionException(SerializationInfo info, StreamingContext context) : base(info, context) + protected EmptyGuidException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a value of a value type is the default value. + /// This exception indicates that a has no value. /// [Serializable] - internal class ArgumentDefaultException : ArgumentException + internal class NullableHasNoValueException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ArgumentDefaultException(string? parameterName = null, string? message = null) : base(message, parameterName) + public NullableHasNoValueException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected ArgumentDefaultException(SerializationInfo info, StreamingContext context) : base(info, context) + protected NullableHasNoValueException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a collection has an invalid number of items. + /// This exception indicates that two references point to the same object. /// [Serializable] - internal class InvalidCollectionCountException : CollectionException + internal class SameObjectReferenceException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public InvalidCollectionCountException(string? parameterName = null, string? message = null) : base(parameterName, message) + public SameObjectReferenceException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected InvalidCollectionCountException(SerializationInfo info, StreamingContext context) : base(info, context) + protected SameObjectReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that the state of a collection is invalid. + /// This exception indicates that a value cannot be cast to another type. /// [Serializable] - internal class CollectionException : ArgumentException + internal class TypeCastException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public CollectionException(string? parameterName = null, string? message = null) : base(message, parameterName) + public TypeCastException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected CollectionException(SerializationInfo info, StreamingContext context) : base(info, context) + protected TypeCastException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a string is in an invalid state. + /// This exception indicates that a string has an invalid length. /// [Serializable] - internal class StringException : ArgumentException + internal class StringLengthException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public StringException(string? parameterName = null, string? message = null) : base(message, parameterName) + public StringLengthException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected StringException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } -#endif - } - - /// - /// This exception indicates that a has no value. - /// - [Serializable] - internal class NullableHasNoValueException : ArgumentException - { - /// - /// Creates a new instance of . - /// - /// The name of the parameter (optional). - /// The message of the exception (optional). - public NullableHasNoValueException(string? parameterName = null, string? message = null) : base(message, parameterName) - { - } - -#if !NET8_0 - /// - protected NullableHasNoValueException(SerializationInfo info, StreamingContext context) : base(info, context) + protected StringLengthException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that two references point to the same object. + /// This exception indicates that a string contains only white space. /// [Serializable] - internal class SameObjectReferenceException : ArgumentException + internal class WhiteSpaceStringException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public SameObjectReferenceException(string? parameterName = null, string? message = null) : base(message, parameterName) + public WhiteSpaceStringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected SameObjectReferenceException(SerializationInfo info, StreamingContext context) : base(info, context) + protected WhiteSpaceStringException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that an URI has an invalid scheme. + /// This exception indicates that an item is not part of a collection. /// [Serializable] - internal class InvalidUriSchemeException : UriException + internal class ValueIsNotOneOfException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public InvalidUriSchemeException(string? parameterName = null, string? message = null) : base(parameterName, message) + public ValueIsNotOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected InvalidUriSchemeException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ValueIsNotOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that an URI is absolute instead of relative. + /// This exception indicates that a string is in an invalid state. /// [Serializable] - internal class AbsoluteUriException : UriException + internal class SubstringException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public AbsoluteUriException(string? parameterName = null, string? message = null) : base(parameterName, message) + public SubstringException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected AbsoluteUriException(SerializationInfo info, StreamingContext context) : base(info, context) + protected SubstringException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that that a GUID is empty. + /// This exception indicates that configuration data is invalid. /// [Serializable] - internal class EmptyGuidException : ArgumentException + internal class InvalidConfigurationException : Exception { /// - /// Creates a new instance of . + /// Initializes a new instance of . /// - /// The name of the parameter (optional). /// The message of the exception (optional). - public EmptyGuidException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The exception that is the cause of this one (optional). + public InvalidConfigurationException(string? message = null, Exception? innerException = null) : base(message, innerException) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected EmptyGuidException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidConfigurationException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that an URI is invalid. + /// This exception indicates that the data is in invalid state. /// [Serializable] - internal class UriException : ArgumentException + internal class InvalidStateException : Exception { /// - /// Creates a new instance of . + /// Creates a new instance of . /// - /// The name of the parameter (optional). /// The message of the exception (optional). - public UriException(string? parameterName = null, string? message = null) : base(message, parameterName) + /// The exception that is the cause of this one (optional). + public InvalidStateException(string? message = null, Exception? innerException = null) : base(message, innerException) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected UriException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that two values are not equal. + /// This exception indicates that a value of a value type is the default value. /// [Serializable] - internal class ValuesNotEqualException : ArgumentException + internal class ArgumentDefaultException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ValuesNotEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) + public ArgumentDefaultException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected ValuesNotEqualException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ArgumentDefaultException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a string contains only white space. + /// This exception indicates that the state of a collection is invalid. /// [Serializable] - internal class WhiteSpaceStringException : StringException + internal class CollectionException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public WhiteSpaceStringException(string? parameterName = null, string? message = null) : base(parameterName, message) + public CollectionException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected WhiteSpaceStringException(SerializationInfo info, StreamingContext context) : base(info, context) + protected CollectionException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that an item is part of a collection. + /// This exception indicates that an URI is invalid. /// [Serializable] - internal class ValueIsOneOfException : ArgumentException + internal class UriException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ValueIsOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) + public UriException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected ValueIsOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) + protected UriException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif @@ -6767,7 +6755,7 @@ public StringDoesNotMatchException(string? parameterName = null, string? message { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// protected StringDoesNotMatchException(SerializationInfo info, StreamingContext context) : base(info, context) { @@ -6776,115 +6764,115 @@ protected StringDoesNotMatchException(SerializationInfo info, StreamingContext c } /// - /// This exception indicates that a value is invalid. + /// This exception indicates that two values are equal. /// [Serializable] - internal class InvalidDateTimeException : ArgumentException + internal class ValuesEqualException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public InvalidDateTimeException(string? parameterName = null, string? message = null) : base(message, parameterName) + public ValuesEqualException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected InvalidDateTimeException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ValuesEqualException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a string is empty. + /// This exception indicates that an Email address is invalid. /// [Serializable] - internal class EmptyStringException : StringException + internal class InvalidEmailAddressException : StringException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public EmptyStringException(string? parameterName = null, string? message = null) : base(parameterName, message) + public InvalidEmailAddressException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected EmptyStringException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidEmailAddressException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that an item is not part of a collection. + /// This exception indicates that an URI is relative instead of absolute. /// [Serializable] - internal class ValueIsNotOneOfException : ArgumentException + internal class RelativeUriException : UriException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public ValueIsNotOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) + public RelativeUriException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected ValueIsNotOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) + protected RelativeUriException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that a value cannot be cast to another type. + /// This exception indicates that an item is part of a collection. /// [Serializable] - internal class TypeCastException : ArgumentException + internal class ValueIsOneOfException : ArgumentException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// /// The name of the parameter (optional). /// The message of the exception (optional). - public TypeCastException(string? parameterName = null, string? message = null) : base(message, parameterName) + public ValueIsOneOfException(string? parameterName = null, string? message = null) : base(message, parameterName) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected TypeCastException(SerializationInfo info, StreamingContext context) : base(info, context) + protected ValueIsOneOfException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif } /// - /// This exception indicates that the data is in invalid state. + /// This exception indicates that a collection has an invalid number of items. /// [Serializable] - internal class InvalidStateException : Exception + internal class InvalidCollectionCountException : CollectionException { /// - /// Creates a new instance of . + /// Creates a new instance of . /// + /// The name of the parameter (optional). /// The message of the exception (optional). - /// The exception that is the cause of this one (optional). - public InvalidStateException(string? message = null, Exception? innerException = null) : base(message, innerException) + public InvalidCollectionCountException(string? parameterName = null, string? message = null) : base(parameterName, message) { } -#if !NET8_0 +#if !NET8_0_OR_GREATER /// - protected InvalidStateException(SerializationInfo info, StreamingContext context) : base(info, context) + protected InvalidCollectionCountException(SerializationInfo info, StreamingContext context) : base(info, context) { } #endif @@ -6900,11 +6888,13 @@ namespace Light.GuardClauses.ExceptionFactory internal static class Throw { /// - /// Throws the default , using the optional parameter name and message. + /// Throws the default indicating that a comparable value must not be + /// greater than the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ArgumentNull(string? parameterName = null, string? message = null) => throw new ArgumentNullException(parameterName, message ?? $"{parameterName ?? "The value"} must not be null."); + public static void MustNotBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than {boundary}, but it actually is {parameter}."); /// /// Throws the default indicating that a string is empty, using the optional /// parameter name and message. @@ -6913,154 +6903,176 @@ internal static class Throw [DoesNotReturn] public static void EmptyString(string? parameterName = null, string? message = null) => throw new EmptyStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be an empty string, but it actually is."); /// - /// Throws the default indicating that an has less than a - /// minimum number of items, using the optional parameter name and message. + /// Throws the default indicating that a value must be approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidMinimumImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have at least a length of {length}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has a length of {parameter.Length}")}."); - } - + public static void MustBeApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); /// - /// Throws the default indicating that a comparable value must not be - /// less than or equal to the given boundary value, using the optional parameter name and message. + /// Throws the default indicating that a value is one of a specified collection + /// of items, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than or equal to {boundary}, but it actually is {parameter}."); + public static void ValueIsOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValueIsOneOfException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The value"} must not be one of the following items").AppendItemsWithNewLine(items).AppendLine($"but it actually is {parameter.ToStringOrNull()}.").ToString()); /// - /// Throws the default indicating that a collection contains the specified item - /// that should not be part of it, using the optional parameter name and message. + /// Throws the default indicating that a reference cannot be downcast, using the + /// optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ExistingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ExistingItemException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must not contain {item.ToStringOrNull()}, but it actually does.").AppendCollectionContent(parameter).ToString()); + public static void InvalidTypeCast(object? parameter, Type targetType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new TypeCastException(parameterName, message ?? $"{parameterName ?? "The value"} {parameter.ToStringOrNull()} cannot be cast to \"{targetType}\"."); /// - /// Throws the default indicating that a comparable value must be greater - /// than the given boundary value, using the optional parameter name and message. + /// Throws the default indicating that a string is not trimmed at the start. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than {boundary}, but it actually is {parameter}."); + public static void NotTrimmedAtStart(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the start, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string is not trimmed at the end. + /// Throws the default indicating that a string does not start with another one, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}."); + public static void StringDoesNotStartWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a value must be approximately - /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// Throws the default indicating that a comparable value must be less + /// than the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); + public static void MustBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than {boundary}, but it actually is {parameter}."); /// - /// Throws the exception that is returned by . + /// Throws the default indicating that a value is the default value of its + /// type, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomException(Func exceptionFactory) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); + public static void ArgumentDefault(string? parameterName = null, string? message = null) => throw new ArgumentDefaultException(parameterName, message ?? $"{parameterName ?? "The value"} must not be the default value."); /// - /// Throws the exception that is returned by . is - /// passed to . + /// Throws the default indicating that a comparable value must be greater + /// than or equal to the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T parameter) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); + public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than or equal to {boundary}, but it actually is {parameter}."); /// - /// Throws the exception that is returned by . and - /// are passed to . + /// Throws the default indicating that a string is not equal to "\n" or "\r\n". /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T1 first, T2 second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); + public static void NotNewLine(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be either \"\\n\" or \"\\r\\n\", but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the exception that is returned by . , - /// , and are passed to . + /// Throws the default indicating that a value is not within a specified + /// range, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomException(Func exceptionFactory, T1 first, T2 second, T3 third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); + public static void MustBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); /// - /// Throws the exception that is returned by . and - /// are passed to . + /// Throws the default indicating that a value is within a specified + /// range, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomSpanException(SpanExceptionFactory exceptionFactory, Span span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); + public static void MustNotBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); /// - /// Throws the exception that is returned by . is - /// passed to . + /// Throws the default indicating that a has + /// no value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); + public static void NullableHasNoValue(string? parameterName = null, string? message = null) => throw new NullableHasNoValueException(parameterName, message ?? $"{parameterName ?? "The nullable"} must have a value, but it actually is null."); /// - /// Throws the exception that is returned by . and - /// are passed to . + /// Throws the default indicating that a GUID is empty, using the optional + /// parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); + public static void EmptyGuid(string? parameterName = null, string? message = null) => throw new EmptyGuidException(parameterName, message ?? $"{parameterName ?? "The value"} must be a valid GUID, but it actually is an empty one."); /// - /// Throws the exception that is returned by . and - /// are passed to . + /// Throws the default indicating that a string contains only white space, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); + public static void WhiteSpaceString(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new WhiteSpaceStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain only white space, but it actually is \"{parameter}\"."); /// - /// Throws the exception that is returned by . , - /// , and are passed to . + /// Throws an using the optional message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second, T third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); + public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); /// - /// Throws the default indicating that a value is not within a specified - /// range, using the optional parameter name and message. + /// Throws the default indicating that a comparable value must not be + /// less than or equal to the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); + public static void MustNotBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than or equal to {boundary}, but it actually is {parameter}."); /// - /// Throws the default indicating that a value is within a specified - /// range, using the optional parameter name and message. + /// Throws the default indicating that a collection has no items, using the + /// optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeInRange(T parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be between {range.CreateRangeDescriptionText("and")}, but it actually is {parameter}."); + public static void EmptyCollection(string? parameterName = null, string? message = null) => throw new EmptyCollectionException(parameterName, message ?? $"{parameterName ?? "The collection"} must not be an empty collection, but it actually is."); /// - /// Throws the default indicating that a comparable value must not be - /// less than the given boundary value, using the optional parameter name and message. + /// Throws the default indicating that a collection has more than a + /// maximum number of items, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than {boundary}, but it actually is {parameter}."); + public static void InvalidMaximumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at most count {count}, but it actually has count {parameter.Count()}."); /// - /// Throws the default indicating that a comparable value must be less - /// than the given boundary value, using the optional parameter name and message. + /// Throws an using the optional message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than {boundary}, but it actually is {parameter}."); + public static void InvalidState(string? message = null) => throw new InvalidStateException(message); + /// + /// Throws the default indicating that a collection has less than a + /// minimum number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidMinimumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at least count {count}, but it actually has count {parameter.Count()}."); + /// + /// Throws the default indicating that a collection contains the specified item + /// that should not be part of it, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ExistingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ExistingItemException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must not contain {item.ToStringOrNull()}, but it actually does.").AppendCollectionContent(parameter).ToString()); + /// + /// Throws the default indicating that a string ends with another one, using the + /// optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringEndsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// /// Throws the default indicating that a comparable value must be greater - /// than or equal to the given boundary value, using the optional parameter name and message. + /// than the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than or equal to {boundary}, but it actually is {parameter}."); + public static void MustBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than {boundary}, but it actually is {parameter}."); + /// + /// Throws the default indicating that two references point to the same + /// object, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void SameObjectReference(T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : class => throw new SameObjectReferenceException(parameterName, message ?? $"{parameterName ?? "The reference"} must not point to object \"{parameter}\", but it actually does."); /// /// Throws the default indicating that a date time is not using /// , using the optional parameter name and message. @@ -7083,11 +7095,54 @@ public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [Calle [DoesNotReturn] public static void MustBeUnspecifiedDateTime(DateTime parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidDateTimeException(parameterName, message ?? $"{parameterName ?? "The date time"} must use kind \"{DateTimeKind.Unspecified}\", but it actually uses \"{parameter.Kind}\" and is \"{parameter:O}\"."); /// - /// Throws the default indicating that a string is not equal to "\n" or "\r\n". + /// Throws the default indicating that a URI is relative instead of absolute, + /// using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void NotNewLine(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be either \"\\n\" or \"\\r\\n\", but it actually is {parameter.ToStringOrNull()}."); + public static void MustBeAbsoluteUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new RelativeUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be an absolute URI, but it actually is \"{parameter}\"."); + /// + /// Throws the default indicating that a URI is absolute instead of relative, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeRelativeUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new AbsoluteUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be a relative URI, but it actually is \"{parameter}\"."); + /// + /// Throws the default indicating that a URI has an unexpected scheme, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void UriMustHaveScheme(Uri parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidUriSchemeException(parameterName, message ?? $"{parameterName ?? "The URI"} must use the scheme \"{scheme}\", but it actually is \"{parameter}\"."); + /// + /// Throws the default indicating that a URI does not use one of a set of + /// expected schemes, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void UriMustHaveOneSchemeOf(Uri parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidUriSchemeException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The URI"} must use one of the following schemes").AppendItemsWithNewLine(schemes).AppendLine($"but it actually is \"{parameter}\".").ToString()); + /// + /// Throws the default indicating that a comparable value must not be + /// less than the given boundary value, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeLessThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be less than {boundary}, but it actually is {parameter}."); + /// + /// Throws the default indicating that a string does not end with another one, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotEndWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + /// + /// Throws the default indicating that a string is not trimmed at the end. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void NotTrimmedAtEnd(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the end, but it actually is {parameter.ToStringOrNull()}."); /// /// Throws the default indicating that a string is not shorter than the given /// length, using the optional parameter name and message. @@ -7131,6 +7186,65 @@ public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [Calle [DoesNotReturn] public static void StringLengthNotInRange(string parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringLengthException(parameterName, message ?? $"{parameterName ?? "The string"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually has length {parameter.Length}."); /// + /// Throws the default indicating that a collection has an invalid + /// number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have count {count}, but it actually has count {parameter.Count()}."); + /// + /// Throws the default indicating that an 's length is not within the + /// given range, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void ImmutableArrayLengthNotInRange(ImmutableArray parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has length {parameter.Length}")}."); + /// + /// Throws the default indicating that a value must not be approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustNotBeApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); + /// + /// Throws the default indicating that an has more than a + /// maximum number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidMaximumImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have at most a length of {length}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has a length of {parameter.Length}")}."); + } + + /// + /// Throws the default indicating that a value must be less than or approximately + /// equal to another value within a specified tolerance, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void MustBeLessThanOrApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); + /// + /// Throws the default indicating that an immutable array has an invalid length, + /// using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + var actualLength = parameter.IsDefault ? 0 : parameter.Length; + throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have length {length}, but it actually has length {actualLength}."); + } + + /// + /// Throws the default indicating that a value is not one of the + /// constants defined in an enum, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void EnumValueNotDefined(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : Enum => throw new EnumValueNotDefinedException(parameterName, message ?? $"{parameterName ?? "The value"} \"{parameter}\" must be one of the defined constants of enum \"{parameter.GetType()}\", but it actually is not."); + /// /// Throws the default indicating that a string is not a substring of another one, /// using the optional parameter name and message. /// @@ -7158,6 +7272,44 @@ public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [Calle [ContractAnnotation("=> halt")] [DoesNotReturn] public static void Substring(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not be a substring of \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + /// + /// Throws the default indicating that a string does start with another one, using + /// the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringStartsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + /// + /// Throws the default indicating that a string does start with another one, using + /// the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringStartsWith(ReadOnlySpan parameter, ReadOnlySpan other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other.ToString()}\" ({comparisonType}), but it actually is {parameter.ToString()}."); + /// + /// Throws an using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void Argument(string? parameterName = null, string? message = null) => throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); + /// + /// Throws the default indicating that a string does not match a regular + /// expression, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void StringDoesNotMatch(string parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringDoesNotMatchException(parameterName, message ?? $"{parameterName ?? "The string"} must match the regular expression \"{regex}\", but it actually is \"{parameter}\"."); + /// + /// Throws the default indicating that an has less than a + /// minimum number of items, using the optional parameter name and message. + /// + [ContractAnnotation("=> halt")] + [DoesNotReturn] + public static void InvalidMinimumImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + { + throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have at least a length of {length}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has a length of {parameter.Length}")}."); + } + /// /// Throws the default indicating that a span has an invalid length, /// using the optional parameter name and message. @@ -7194,151 +7346,80 @@ public static void MustBeGreaterThanOrEqualTo(T parameter, T boundary, [Calle [DoesNotReturn] public static void SpanMustBeShorterThanOrEqualTo(ReadOnlySpan parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The span"} must be shorter than or equal to {length}, but it actually has length {parameter.Length}."); /// - /// Throws the default indicating that a collection has less than a - /// minimum number of items, using the optional parameter name and message. + /// Throws the default indicating that a comparable value must not be + /// greater than or equal to the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidMinimumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at least count {count}, but it actually has count {parameter.Count()}."); + public static void MustNotBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than or equal to {boundary}, but it actually is {parameter}."); /// - /// Throws the default indicating that a string contains only white space, - /// using the optional parameter name and message. + /// Throws the default indicating that a string is not a valid file extension. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void WhiteSpaceString(string parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new WhiteSpaceStringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain only white space, but it actually is \"{parameter}\"."); + public static void NotFileExtension(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws an using the optional message. + /// Throws the default indicating that a string is not a valid file extension. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidState(string? message = null) => throw new InvalidStateException(message); + public static void NotFileExtension(ReadOnlySpan parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToString()}."); /// - /// Throws the default indicating that a URI is relative instead of absolute, - /// using the optional parameter name and message. + /// Throws the default indicating that two values are not equal, using the + /// optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeAbsoluteUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new RelativeUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be an absolute URI, but it actually is \"{parameter}\"."); + public static void ValuesNotEqual(T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValuesNotEqualException(parameterName, message ?? $"{parameterName ?? "The value"} must be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a URI is absolute instead of relative, - /// using the optional parameter name and message. + /// Throws the default indicating that two values are equal, using the optional + /// parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustBeRelativeUri(Uri parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new AbsoluteUriException(parameterName, message ?? $"{parameterName ?? "The URI"} must be a relative URI, but it actually is \"{parameter}\"."); + public static void ValuesEqual(T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValuesEqualException(parameterName, message ?? $"{parameterName ?? "The value"} must not be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a URI has an unexpected scheme, - /// using the optional parameter name and message. + /// Throws the default , using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void UriMustHaveScheme(Uri parameter, string scheme, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidUriSchemeException(parameterName, message ?? $"{parameterName ?? "The URI"} must use the scheme \"{scheme}\", but it actually is \"{parameter}\"."); + public static void ArgumentNull(string? parameterName = null, string? message = null) => throw new ArgumentNullException(parameterName, message ?? $"{parameterName ?? "The value"} must not be null."); /// - /// Throws the default indicating that a URI does not use one of a set of - /// expected schemes, using the optional parameter name and message. + /// Throws the default indicating that a collection is not containing the + /// specified item, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void UriMustHaveOneSchemeOf(Uri parameter, IEnumerable schemes, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidUriSchemeException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The URI"} must use one of the following schemes").AppendItemsWithNewLine(schemes).AppendLine($"but it actually is \"{parameter}\".").ToString()); + public static void MissingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new MissingItemException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must contain {item.ToStringOrNull()}, but it actually does not.").AppendCollectionContent(parameter).ToString()); /// - /// Throws the default indicating that a collection has no items, using the - /// optional parameter name and message. + /// Throws the default indicating that a string does not contain another string as + /// a substring, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void EmptyCollection(string? parameterName = null, string? message = null) => throw new EmptyCollectionException(parameterName, message ?? $"{parameterName ?? "The collection"} must not be an empty collection, but it actually is."); + public static void StringDoesNotContain(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a string is not trimmed at the start. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotTrimmedAtStart(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed at the start, but it actually is {parameter.ToStringOrNull()}."); - /// - /// Throws an using the optional message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidOperation(string? message = null) => throw new InvalidOperationException(message); - /// - /// Throws the default indicating that a comparable value must be less - /// than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than or equal to {boundary}, but it actually is {parameter}."); - /// - /// Throws the default indicating that two values are not equal, using the - /// optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValuesNotEqual(T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValuesNotEqualException(parameterName, message ?? $"{parameterName ?? "The value"} must be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); - /// - /// Throws the default indicating that two values are equal, using the optional - /// parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ValuesEqual(T parameter, T other, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValuesEqualException(parameterName, message ?? $"{parameterName ?? "The value"} must not be equal to {other.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); - /// - /// Throws the default indicating that a value is not one of the - /// constants defined in an enum, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void EnumValueNotDefined(T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : Enum => throw new EnumValueNotDefinedException(parameterName, message ?? $"{parameterName ?? "The value"} \"{parameter}\" must be one of the defined constants of enum \"{parameter.GetType()}\", but it actually is not."); - /// - /// Throws the default indicating that a value must be less than or approximately - /// equal to another value within a specified tolerance, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustBeLessThanOrApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); - /// - /// Throws the default indicating that a value is the default value of its - /// type, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void ArgumentDefault(string? parameterName = null, string? message = null) => throw new ArgumentDefaultException(parameterName, message ?? $"{parameterName ?? "The value"} must not be the default value."); - /// - /// Throws an using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void Argument(string? parameterName = null, string? message = null) => throw new ArgumentException(message ?? $"{parameterName ?? "The value"} is invalid.", parameterName); - /// - /// Throws the default indicating that a collection is not containing the - /// specified item, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MissingItem(IEnumerable parameter, TItem item, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new MissingItemException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The collection"} must contain {item.ToStringOrNull()}, but it actually does not.").AppendCollectionContent(parameter).ToString()); - /// - /// Throws the default indicating that a value is one of a specified collection - /// of items, using the optional parameter name and message. + /// Throws the default indicating that a string does not contain another string as + /// a substring, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ValueIsOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValueIsOneOfException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The value"} must not be one of the following items").AppendItemsWithNewLine(items).AppendLine($"but it actually is {parameter.ToStringOrNull()}.").ToString()); + public static void StringDoesNotContain(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()} ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that two references point to the same - /// object, using the optional parameter name and message. + /// Throws the default indicating that a string does contain another string as a + /// substring, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void SameObjectReference(T? parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : class => throw new SameObjectReferenceException(parameterName, message ?? $"{parameterName ?? "The reference"} must not point to object \"{parameter}\", but it actually does."); + public static void StringContains(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a GUID is empty, using the optional - /// parameter name and message. + /// Throws the default indicating that a string does contain another string as a + /// substring, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void EmptyGuid(string? parameterName = null, string? message = null) => throw new EmptyGuidException(parameterName, message ?? $"{parameterName ?? "The value"} must be a valid GUID, but it actually is an empty one."); + public static void StringContains(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); /// /// Throws the default indicating that a string is not trimmed. /// @@ -7346,21 +7427,6 @@ public static void SameObjectReference(T? parameter, [CallerArgumentExpressio [DoesNotReturn] public static void NotTrimmed(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be trimmed, but it actually is {parameter.ToStringOrNull()}."); /// - /// Throws the default indicating that a comparable value must not be - /// greater than or equal to the given boundary value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeGreaterThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than or equal to {boundary}, but it actually is {parameter}."); - /// - /// Throws the default indicating that a has - /// no value, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NullableHasNoValue(string? parameterName = null, string? message = null) => throw new NullableHasNoValueException(parameterName, message ?? $"{parameterName ?? "The nullable"} must have a value, but it actually is null."); - /// /// Throws an using the optional message. /// [ContractAnnotation("=> halt")] @@ -7373,25 +7439,6 @@ public static void MustNotBeGreaterThanOrEqualTo(T parameter, T boundary, [Ca [DoesNotReturn] public static void InvalidEmailAddress(ReadOnlySpan parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidEmailAddressException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid email address, but it actually is \"{parameter.ToString()}\"."); /// - /// Throws the default indicating that a collection has more than a - /// maximum number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidMaximumCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have at most count {count}, but it actually has count {parameter.Count()}."); - /// - /// Throws the default indicating that a string is not a valid file extension. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotFileExtension(string? parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToStringOrNull()}."); - /// - /// Throws the default indicating that a string is not a valid file extension. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void NotFileExtension(ReadOnlySpan parameter, string? parameterName, string? message) => throw new StringException(parameterName, message ?? $"{parameterName ?? "The string"} must be a valid file extension, but it actually is {parameter.ToString()}."); - /// /// Throws the default indicating that a value must be greater than or approximately /// equal to another value within a specified tolerance, using the optional parameter name and message. /// @@ -7399,52 +7446,13 @@ public static void MustNotBeGreaterThanOrEqualTo(T parameter, T boundary, [Ca [DoesNotReturn] public static void MustBeGreaterThanOrApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be greater than or approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); /// - /// Throws the default indicating that a string does not end with another one, - /// using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotEndWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - /// - /// Throws the default indicating that a reference cannot be downcast, using the - /// optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidTypeCast(object? parameter, Type targetType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new TypeCastException(parameterName, message ?? $"{parameterName ?? "The value"} {parameter.ToStringOrNull()} cannot be cast to \"{targetType}\"."); - /// - /// Throws the default indicating that a collection has an invalid - /// number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidCollectionCount(IEnumerable parameter, int count, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The collection"} must have count {count}, but it actually has count {parameter.Count()}."); - /// - /// Throws the default indicating that a string ends with another one, using the - /// optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringEndsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not end with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); - /// - /// Throws the default indicating that a string does not match a regular - /// expression, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void StringDoesNotMatch(string parameter, Regex regex, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new StringDoesNotMatchException(parameterName, message ?? $"{parameterName ?? "The string"} must match the regular expression \"{regex}\", but it actually is \"{parameter}\"."); - /// - /// Throws the default indicating that an immutable array has an invalid length, - /// using the optional parameter name and message. + /// Throws the default indicating that a comparable value must be less + /// than or equal to the given boundary value, using the optional parameter name and message. /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void InvalidImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - var actualLength = parameter.IsDefault ? 0 : parameter.Length; - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have length {length}, but it actually has length {actualLength}."); - } - + public static void MustBeLessThanOrEqualTo(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) + where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must be less than or equal to {boundary}, but it actually is {parameter}."); /// /// Throws the default indicating that a value is not one of a specified /// collection of items, using the optional parameter name and message. @@ -7453,87 +7461,67 @@ public static void InvalidImmutableArrayLength(ImmutableArray parameter, i [DoesNotReturn] public static void ValueNotOneOf(TItem parameter, IEnumerable items, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ValueIsNotOneOfException(parameterName, message ?? new StringBuilder().AppendLine($"{parameterName ?? "The value"} must be one of the following items").AppendItemsWithNewLine(items).AppendLine($"but it actually is {parameter.ToStringOrNull()}.").ToString()); /// - /// Throws the default indicating that an has more than a - /// maximum number of items, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void InvalidMaximumImmutableArrayLength(ImmutableArray parameter, int length, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - { - throw new InvalidCollectionCountException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have at most a length of {length}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has a length of {parameter.Length}")}."); - } - - /// - /// Throws the default indicating that a value must not be approximately - /// equal to another value within a specified tolerance, using the optional parameter name and message. - /// - [ContractAnnotation("=> halt")] - [DoesNotReturn] - public static void MustNotBeApproximately(T parameter, T other, T tolerance, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be approximately equal to {other} with a tolerance of {tolerance}, but it actually is {parameter}."); - /// - /// Throws the default indicating that a string does start with another one, using - /// the optional parameter name and message. + /// Throws the exception that is returned by . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringStartsWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void CustomException(Func exceptionFactory) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(); /// - /// Throws the default indicating that a string does start with another one, using - /// the optional parameter name and message. + /// Throws the exception that is returned by . is + /// passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringStartsWith(ReadOnlySpan parameter, ReadOnlySpan other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not start with \"{other.ToString()}\" ({comparisonType}), but it actually is {parameter.ToString()}."); + public static void CustomException(Func exceptionFactory, T parameter) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(parameter); /// - /// Throws the default indicating that a comparable value must not be - /// greater than the given boundary value, using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void MustNotBeGreaterThan(T parameter, T boundary, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) - where T : IComparable => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The value"} must not be greater than {boundary}, but it actually is {parameter}."); + public static void CustomException(Func exceptionFactory, T1 first, T2 second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); /// - /// Throws the default indicating that a string does not contain another string as - /// a substring, using the optional parameter name and message. + /// Throws the exception that is returned by . , + /// , and are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringDoesNotContain(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()}, but it actually is {parameter.ToStringOrNull()}."); + public static void CustomException(Func exceptionFactory, T1 first, T2 second, T3 third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); /// - /// Throws the default indicating that a string does not contain another string as - /// a substring, using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringDoesNotContain(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must contain {substring.ToStringOrNull()} ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void CustomSpanException(SpanExceptionFactory exceptionFactory, Span span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory)).Invoke(span, value); /// - /// Throws the default indicating that a string does contain another string as a - /// substring, using the optional parameter name and message. + /// Throws the exception that is returned by . is + /// passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringContains(string parameter, string substring, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring, but it actually is {parameter.ToStringOrNull()}."); + public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span); /// - /// Throws the default indicating that a string does contain another string as a - /// substring, using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringContains(string parameter, string substring, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must not contain {substring.ToStringOrNull()} as a substring ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void CustomSpanException(ReadOnlySpanExceptionFactory exceptionFactory, ReadOnlySpan span, T value) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(span, value); /// - /// Throws the default indicating that an 's length is not within the - /// given range, using the optional parameter name and message. + /// Throws the exception that is returned by . and + /// are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void ImmutableArrayLengthNotInRange(ImmutableArray parameter, Range range, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new ArgumentOutOfRangeException(parameterName, message ?? $"{parameterName ?? "The immutable array"} must have its length in between {range.CreateRangeDescriptionText("and")}, but it actually {(parameter.IsDefault ? "has no length because it is the default instance" : $"has length {parameter.Length}")}."); + public static void CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second); /// - /// Throws the default indicating that a string does not start with another one, - /// using the optional parameter name and message. + /// Throws the exception that is returned by . , + /// , and are passed to . /// [ContractAnnotation("=> halt")] [DoesNotReturn] - public static void StringDoesNotStartWith(string parameter, string other, StringComparison comparisonType, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) => throw new SubstringException(parameterName, message ?? $"{parameterName ?? "The string"} must start with \"{other}\" ({comparisonType}), but it actually is {parameter.ToStringOrNull()}."); + public static void CustomSpanException(ReadOnlySpansExceptionFactory exceptionFactory, ReadOnlySpan first, ReadOnlySpan second, T third) => throw exceptionFactory.MustNotBeNull(nameof(exceptionFactory))(first, second, third); } /// @@ -7561,431 +7549,400 @@ public static void MustNotBeGreaterThan(T parameter, T boundary, [CallerArgum namespace Light.GuardClauses.FrameworkExtensions { /// - /// Represents a builder for the algorithm that does not allocate. - /// Should only be used in cases where the overload for sixteen values is not enough or a dedicated - /// initial hash must be provided (e.g. for test reasons). - /// Instantiate the builder with the method. You have to instantiate a new builder - /// for each hash code that you want to calculate. + /// Provides extension methods for and to easily assembly error messages. /// - internal struct MultiplyAddHashBuilder + internal static class TextExtensions { - private int _hash; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private MultiplyAddHashBuilder(int initialHash) => _hash = initialHash; /// - /// Combines the given value into the hash using the method. + /// Gets the default NewLineSeparator. This value is $",{Environment.NewLine}". /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiplyAddHashBuilder CombineIntoHash(T value) + public static readonly string DefaultNewLineSeparator = ',' + Environment.NewLine; + /// + /// Gets the list of types that will not be surrounded by quotation marks in error messages. + /// + public static readonly ReadOnlyCollection UnquotedTypes = new([typeof(int), typeof(long), typeof(short), typeof(sbyte), typeof(uint), typeof(ulong), typeof(ushort), typeof(byte), typeof(bool), typeof(double), typeof(decimal), typeof(float), ]); + private static bool IsUnquotedType() { - MultiplyAddHash.CombineIntoHash(ref _hash, value); - return this; + if (typeof(T) == typeof(int)) + { + return true; + } + + if (typeof(T) == typeof(long)) + { + return true; + } + + if (typeof(T) == typeof(short)) + { + return true; + } + + if (typeof(T) == typeof(sbyte)) + { + return true; + } + + if (typeof(T) == typeof(uint)) + { + return true; + } + + if (typeof(T) == typeof(ulong)) + { + return true; + } + + if (typeof(T) == typeof(ushort)) + { + return true; + } + + if (typeof(T) == typeof(byte)) + { + return true; + } + + if (typeof(T) == typeof(bool)) + { + return true; + } + + if (typeof(T) == typeof(double)) + { + return true; + } + + if (typeof(T) == typeof(decimal)) + { + return true; + } + + if (typeof(T) == typeof(float)) + { + return true; + } + + return false; } /// - /// Returns the calculated hash code. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int BuildHash() => _hash; - /// - /// Initializes a new instance of with the specified initial hash. + /// Returns the string representation of , or if is null. + /// If the type of is not one of , then quotation marks will be put around the string representation. /// + /// The item whose string representation should be returned. + /// The text that is returned when is null (defaults to "null"). [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new(initialHash); - } - - /// - /// Provides extension methods for instances. - /// - internal static class ExpressionExtensions - { + [ContractAnnotation("=> notnull")] + public static string ToStringOrNull(this T value, string nullText = "null") => value?.ToStringRepresentation() ?? nullText; /// - /// Extracts the from an expression of the shape "object => object.Property". + /// Returns the string representation of . This is done by calling . If the type of + /// is not one of , then the resulting string will be wrapped in quotation marks. /// - /// The object type. - /// The type of the property. - /// The expression where the property info will be extracted from. - /// Thrown when is null. - /// - /// Throw when the is not of the shape "object => object.Property". - /// - public static PropertyInfo ExtractProperty(// ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests - [NotNull][ValidatedNotNull] this Expression> expression) + /// The value whose string representation is requested. + [ContractAnnotation("value:null => halt; value:notnull => notnull")] + public static string? ToStringRepresentation([NotNull][ValidatedNotNull] this T value) { - expression.MustNotBeNull(nameof(expression)); - var memberExpression = expression.Body as MemberExpression; - if (!(memberExpression?.Member is PropertyInfo propertyInfo)) + value.MustNotBeNullReference(nameof(value)); + var content = value.ToString(); + if (IsUnquotedType() || content.IsNullOrEmpty()) { - throw new ArgumentException("The specified expression is not valid. Please use an expression like the following one: o => o.Property", nameof(expression)); + return content; } - return propertyInfo; + // ReSharper disable UseIndexFromEndExpression -- not possible in netstandard2.0 + if (content.Length <= 126) + { + Span span = stackalloc char[content.Length + 2]; + span[0] = span[span.Length - 1] = '"'; + content.AsSpan().CopyTo(span.Slice(1, content.Length)); + return span.ToString(); + } + + var contentWithQuotationMarks = new char[content.Length + 2]; + contentWithQuotationMarks[0] = contentWithQuotationMarks[contentWithQuotationMarks.Length - 1] = '"'; + // ReSharper restore UseIndexFromEndExpression + content.CopyTo(0, contentWithQuotationMarks, 1, content.Length); + return new string (contentWithQuotationMarks); } /// - /// Extracts the from an expression of the shape "object => object.Field". + /// Appends the content of the collection with the specified header line to the string builder. + /// Each item is on a new line. /// - /// The object type. - /// The type of the field. - /// The expression where the field info will be extracted from. - /// Thrown when is null. - /// - /// Throw when the is not of the shape "object => object.Field". - /// - public static FieldInfo ExtractField(// ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests - [NotNull][ValidatedNotNull] this Expression> expression) + /// The item type of the collection. + /// The string builder that the content is appended to. + /// The collection whose items will be appended to the string builder. + /// The string that will be placed before the actual items as a header. + /// The value indicating if a new line is added after the last item. This value defaults to true. + /// Thrown when or is null. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendCollectionContent([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(headerLine).AppendItemsWithNewLine(items, finishWithNewLine: finishWithNewLine); + // ReSharper restore RedundantNullableFlowAttribute + /// + /// Appends the string representations of the specified items to the string builder. + /// + /// The string builder where the items will be appended to. + /// The items to be appended. + /// The characters used to separate the items. Defaults to ", " and is not appended after the last item. + /// The text that is appended to the string builder when is empty. Defaults to "empty collection". + /// Thrown when or is null. + [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendItems([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") + // ReSharper restore RedundantNullableFlowAttribute { - expression.MustNotBeNull(nameof(expression)); - var memberExpression = expression.Body as MemberExpression; - if (!(memberExpression?.Member is FieldInfo fieldInfo)) + stringBuilder.MustNotBeNull(nameof(stringBuilder)); + var list = items.MustNotBeNull(nameof(items)).AsList(); + var currentIndex = 0; + var itemsCount = list.Count; + if (itemsCount == 0) { - throw new ArgumentException("The specified expression is not valid. Please use an expression like the following one: o => o.Field", nameof(expression)); + return stringBuilder.Append(emptyCollectionText); } - return fieldInfo; + while (true) + { + stringBuilder.Append(list[currentIndex].ToStringOrNull()); + if (currentIndex < itemsCount - 1) + { + stringBuilder.Append(itemSeparator); + } + else + { + return stringBuilder; + } + + ++currentIndex; + } } - } - /// - /// The class represents a simple non-cryptographic hash function that uses a prime number - /// as seed and then manipulates this value by constantly performing hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0); - /// for each given value. It is implemented according to the guidelines of Jon Skeet as stated in this Stack Overflow - /// answer: http://stackoverflow.com/a/263416/1560623. IMPORTANT: do not persist any hash codes and rely on them - /// to stay the same. Hash codes should only be used in memory within a single process session, usually for the use - /// in dictionaries (hash tables) and sets. This algorithm, especially the prime numbers can change even during minor - /// releases of Light.GuardClauses. - /// - internal static class MultiplyAddHash - { - /// - /// This prime number is used as an initial (seed) value when calculating hash codes. Its value is 1322837333. - /// - public const int FirstPrime = 1322837333; - /// - /// The second prime number (397) used for hash code generation. It is applied using the following statement: - /// hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0);. - /// It is the same value that ReSharper (2018.1) uses for hash code generation. - /// - public const int SecondPrime = 397; /// - /// Creates a hash code from the two specified values. + /// Appends the string representations of the specified items to the string builder. Each item is on its own line. /// + /// The string builder where the items will be appended to. + /// The items to be appended. + /// The text that is appended to the string builder when is empty. Defaults to "empty collection". + /// The value indicating if a new line is added after the last item. This value defaults to true. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - return hash; - } - + [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendItemsWithNewLine([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => stringBuilder.AppendItems(items, DefaultNewLineSeparator, emptyCollectionText).AppendLineIf(finishWithNewLine); + // ReSharper restore RedundantNullableFlowAttribute /// - /// Creates a hash code from the three specified values. + /// Appends the value to the specified string builder if the condition is true. /// + /// The string builder where will be appended to. + /// The boolean value indicating whether the append operation will be performed or not. + /// The value to be appended to the string builder. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3) + [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] + public static StringBuilder AppendIf(// ReSharper disable once RedundantNullableFlowAttribute + [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - return hash; + if (condition) + { + stringBuilder.MustNotBeNull(nameof(stringBuilder)).Append(value); + } + + return stringBuilder; } /// - /// Creates a hash code from the four specified values. + /// Appends the value followed by a new line separator to the specified string builder if the condition is true. /// + /// The string builder where will be appended to. + /// The boolean value indicating whether the append operation will be performed or not. + /// The value to be appended to the string builder (optional). This value defaults to an empty string. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4) + [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] + public static StringBuilder AppendLineIf(// ReSharper disable once RedundantNullableFlowAttribute + [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - return hash; + if (condition) + { + stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(value); + } + + return stringBuilder; } /// - /// Creates a hash code from the five specified values. + /// Appends the messages of the and its nested exceptions to the + /// specified . /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) + /// Thrown when any parameter is null. + // ReSharper disable RedundantNullableFlowAttribute + public static StringBuilder AppendExceptionMessages([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] Exception exception) + // ReSharper restore RedundantNullableFlowAttribute { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - return hash; + stringBuilder.MustNotBeNull(nameof(stringBuilder)); + exception.MustNotBeNull(nameof(exception)); + while (true) + { + // ReSharper disable once PossibleNullReferenceException + stringBuilder.AppendLine(exception.Message); + if (exception.InnerException is null) + { + return stringBuilder; + } + + stringBuilder.AppendLine(); + exception = exception.InnerException; + } } /// - /// Creates a hash code from the six specified values. + /// Formats all messages of the and its nested exceptions into + /// a single string. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - return hash; - } - + /// Thrown when is null. + // ReSharper disable once RedundantNullableFlowAttribute + public static string GetAllExceptionMessages([NotNull][ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); /// - /// Creates a hash code from the seven specified values. + /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space + /// of the provided strings. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) + public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - return hash; + if (ReferenceEquals(x, y)) + { + return true; + } + + if (x is null || y is null) + { + return false; + } + + if (x.Length == 0) + { + return y.Length == 0; + } + + var indexX = 0; + var indexY = 0; + bool wasXSuccessful; + bool wasYSuccessful; + // This condition of the while loop actually has to use the single '&' operator because + // y.TryAdvanceToNextNonWhiteSpaceCharacter must be called even though it already returned + // false on x. Otherwise, the 'wasXSuccessful == wasYSuccessful' comparison would not return + // the desired result. + while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) + { + if (x[indexX++] != y[indexY++]) + { + return false; + } + } + + return wasXSuccessful == wasYSuccessful; } /// - /// Creates a hash code from the eight specified values. + /// Checks if the two strings are equal using ordinal sorting rules, ignoring the case of the letters + /// as well as ignoring the white space of the provided strings. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + public static bool EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(this string? x, string? y) { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - return hash; + if (ReferenceEquals(x, y)) + { + return true; + } + + if (x is null || y is null) + { + return false; + } + + if (x.Length == 0) + { + return y.Length == 0; + } + + var indexX = 0; + var indexY = 0; + bool wasXSuccessful; + bool wasYSuccessful; + // This condition of the while loop actually has to use the single '&' operator because + // y.TryAdvanceToNextNonWhiteSpaceCharacter must be called even though it already returned + // false on x. Otherwise, the 'wasXSuccessful == wasYSuccessful' comparison would not return + // the desired result. + while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) + { + if (char.ToLowerInvariant(x[indexX++]) != char.ToLowerInvariant(y[indexY++])) + { + return false; + } + } + + return wasXSuccessful == wasYSuccessful; } - /// - /// Creates a hash code from the nine specified values. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9) + private static bool TryAdvanceToNextNonWhiteSpaceCharacter(this string @string, ref int currentIndex) { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - return hash; + while (currentIndex < @string.Length) + { + if (!char.IsWhiteSpace(@string[currentIndex])) + { + return true; + } + + ++currentIndex; + } + + return false; } + } + internal static partial class StringExtensions + { /// - /// Creates a hash code from the ten specified values. + /// Checks if the string contains the specified value using the given comparison type. /// + /// The string to be checked. + /// The other string. + /// One of the enumeration values that specifies the rules for the search. + /// True if contains , else false. + /// Thrown when or is null. + /// Thrown when is not a valid value. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - return hash; - } + [ContractAnnotation("string:null => halt; value:null => halt")] + public static bool Contains(// ReSharper disable once RedundantNullableFlowAttribute -- Caller might have NRTs turned off + [NotNull][ValidatedNotNull] this string @string, string value, StringComparison comparisonType) => @string.MustNotBeNull(nameof(@string)).IndexOf(value.MustNotBeNull(nameof(value)), comparisonType) >= 0; + } + /// + /// Provides extension methods for the interface. + /// + internal static class EnumerableExtensions + { /// - /// Creates a hash code from the eleven specified values. + /// Tries to cast the specified enumerable to an , or + /// creates a new containing the enumerable items. /// + /// The item type of the enumerable. + /// The enumerable to be transformed. + /// The list containing the items of the enumerable. + /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - return hash; - } - + [ContractAnnotation("source:null => halt; source:notnull => notnull")] + // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests + public static IList AsList([NotNull][ValidatedNotNull] this IEnumerable source) => source as IList ?? source.ToList(); /// - /// Creates a hash code from the eleven specified values. + /// Tries to cast the specified enumerable to an , or + /// creates a new collection containing the enumerable items by calling the specified delegate. /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - CombineIntoHash(ref hash, value12); - return hash; - } - - /// - /// Creates a hash code from the thirteen specified values. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - CombineIntoHash(ref hash, value12); - CombineIntoHash(ref hash, value13); - return hash; - } - - /// - /// Creates a hash code from the fourteen specified values. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - CombineIntoHash(ref hash, value12); - CombineIntoHash(ref hash, value13); - CombineIntoHash(ref hash, value14); - return hash; - } - - /// - /// Creates a hash code from the fifteen specified values. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14, T15 value15) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - CombineIntoHash(ref hash, value12); - CombineIntoHash(ref hash, value13); - CombineIntoHash(ref hash, value14); - CombineIntoHash(ref hash, value15); - return hash; - } - - /// - /// Creates a hash code from the sixteen specified values. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14, T15 value15, T16 value16) - { - var hash = FirstPrime; - CombineIntoHash(ref hash, value1); - CombineIntoHash(ref hash, value2); - CombineIntoHash(ref hash, value3); - CombineIntoHash(ref hash, value4); - CombineIntoHash(ref hash, value5); - CombineIntoHash(ref hash, value6); - CombineIntoHash(ref hash, value7); - CombineIntoHash(ref hash, value8); - CombineIntoHash(ref hash, value9); - CombineIntoHash(ref hash, value10); - CombineIntoHash(ref hash, value11); - CombineIntoHash(ref hash, value12); - CombineIntoHash(ref hash, value13); - CombineIntoHash(ref hash, value14); - CombineIntoHash(ref hash, value15); - CombineIntoHash(ref hash, value16); - return hash; - } - - /// - /// Mutates the given hash with the specified value using the following statement: - /// hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0);. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void CombineIntoHash(ref int hash, T value) => hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0); - } - - /// - /// Provides extension methods for the interface. - /// - internal static class EnumerableExtensions - { - /// - /// Tries to cast the specified enumerable to an , or - /// creates a new containing the enumerable items. - /// - /// The item type of the enumerable. - /// The enumerable to be transformed. - /// The list containing the items of the enumerable. - /// Thrown when is null. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("source:null => halt; source:notnull => notnull")] - // ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests - public static IList AsList([NotNull][ValidatedNotNull] this IEnumerable source) => source as IList ?? source.ToList(); - /// - /// Tries to cast the specified enumerable to an , or - /// creates a new collection containing the enumerable items by calling the specified delegate. - /// - /// The item type of the collection. - /// The enumerable that will be converted to . - /// The delegate that creates the collection containing the specified items. - /// The cast enumerable, or a new collection containing the enumerable items. - /// Thrown when or is null. + /// The item type of the collection. + /// The enumerable that will be converted to . + /// The delegate that creates the collection containing the specified items. + /// The cast enumerable, or a new collection containing the enumerable items. + /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] [ContractAnnotation("source:null => halt; source:notnull => notnull; createCollection:null => halt")] public static IList AsList(// ReSharper disable once RedundantNullableFlowAttribute -- NotNull has an effect, see Issue72NotNullAttributeTests @@ -8257,355 +8214,411 @@ internal static bool ContainsViaForeach(this IEnumerable items, TI } /// - /// Provides extension methods for and to easily assembly error messages. + /// Provides extension methods for instances. /// - internal static class TextExtensions + internal static class ExpressionExtensions { /// - /// Gets the default NewLineSeparator. This value is $",{Environment.NewLine}". - /// - public static readonly string DefaultNewLineSeparator = ',' + Environment.NewLine; - /// - /// Gets the list of types that will not be surrounded by quotation marks in error messages. + /// Extracts the from an expression of the shape "object => object.Property". /// - public static readonly ReadOnlyCollection UnquotedTypes = new([typeof(int), typeof(long), typeof(short), typeof(sbyte), typeof(uint), typeof(ulong), typeof(ushort), typeof(byte), typeof(bool), typeof(double), typeof(decimal), typeof(float), ]); - private static bool IsUnquotedType() + /// The object type. + /// The type of the property. + /// The expression where the property info will be extracted from. + /// Thrown when is null. + /// + /// Throw when the is not of the shape "object => object.Property". + /// + public static PropertyInfo ExtractProperty(// ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + [NotNull][ValidatedNotNull] this Expression> expression) { - if (typeof(T) == typeof(int)) + expression.MustNotBeNull(nameof(expression)); + var memberExpression = expression.Body as MemberExpression; + if (!(memberExpression?.Member is PropertyInfo propertyInfo)) { - return true; + throw new ArgumentException("The specified expression is not valid. Please use an expression like the following one: o => o.Property", nameof(expression)); } - if (typeof(T) == typeof(long)) - { - return true; - } + return propertyInfo; + } - if (typeof(T) == typeof(short)) + /// + /// Extracts the from an expression of the shape "object => object.Field". + /// + /// The object type. + /// The type of the field. + /// The expression where the field info will be extracted from. + /// Thrown when is null. + /// + /// Throw when the is not of the shape "object => object.Field". + /// + public static FieldInfo ExtractField(// ReSharper disable once RedundantNullableFlowAttribute - NotNull is not redundant, see Issue72NotNullAttributeTests + [NotNull][ValidatedNotNull] this Expression> expression) + { + expression.MustNotBeNull(nameof(expression)); + var memberExpression = expression.Body as MemberExpression; + if (!(memberExpression?.Member is FieldInfo fieldInfo)) { - return true; + throw new ArgumentException("The specified expression is not valid. Please use an expression like the following one: o => o.Field", nameof(expression)); } - if (typeof(T) == typeof(sbyte)) - { - return true; - } + return fieldInfo; + } + } - if (typeof(T) == typeof(uint)) - { - return true; - } + /// + /// Provides extension methods for the class. + /// + // ReSharper disable once RedundantTypeDeclarationBody -- required for Source Code Transformation + internal static partial class StringExtensions + { + } - if (typeof(T) == typeof(ulong)) - { - return true; - } + /// + /// The class represents a simple non-cryptographic hash function that uses a prime number + /// as seed and then manipulates this value by constantly performing hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0); + /// for each given value. It is implemented according to the guidelines of Jon Skeet as stated in this Stack Overflow + /// answer: http://stackoverflow.com/a/263416/1560623. IMPORTANT: do not persist any hash codes and rely on them + /// to stay the same. Hash codes should only be used in memory within a single process session, usually for the use + /// in dictionaries (hash tables) and sets. This algorithm, especially the prime numbers can change even during minor + /// releases of Light.GuardClauses. + /// + internal static class MultiplyAddHash + { + /// + /// This prime number is used as an initial (seed) value when calculating hash codes. Its value is 1322837333. + /// + public const int FirstPrime = 1322837333; + /// + /// The second prime number (397) used for hash code generation. It is applied using the following statement: + /// hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0);. + /// It is the same value that ReSharper (2018.1) uses for hash code generation. + /// + public const int SecondPrime = 397; + /// + /// Creates a hash code from the two specified values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + return hash; + } - if (typeof(T) == typeof(ushort)) - { - return true; - } - - if (typeof(T) == typeof(byte)) - { - return true; - } - - if (typeof(T) == typeof(bool)) - { - return true; - } - - if (typeof(T) == typeof(double)) - { - return true; - } - - if (typeof(T) == typeof(decimal)) - { - return true; - } - - if (typeof(T) == typeof(float)) - { - return true; - } - - return false; + /// + /// Creates a hash code from the three specified values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + return hash; } /// - /// Returns the string representation of , or if is null. - /// If the type of is not one of , then quotation marks will be put around the string representation. + /// Creates a hash code from the four specified values. /// - /// The item whose string representation should be returned. - /// The text that is returned when is null (defaults to "null"). [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("=> notnull")] - public static string ToStringOrNull(this T value, string nullText = "null") => value?.ToStringRepresentation() ?? nullText; + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + return hash; + } + /// - /// Returns the string representation of . This is done by calling . If the type of - /// is not one of , then the resulting string will be wrapped in quotation marks. + /// Creates a hash code from the five specified values. /// - /// The value whose string representation is requested. - [ContractAnnotation("value:null => halt; value:notnull => notnull")] - public static string? ToStringRepresentation([NotNull][ValidatedNotNull] this T value) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) { - value.MustNotBeNullReference(nameof(value)); - var content = value.ToString(); - if (IsUnquotedType() || content.IsNullOrEmpty()) - { - return content; - } - - // ReSharper disable UseIndexFromEndExpression -- not possible in netstandard2.0 - if (content.Length <= 126) - { - Span span = stackalloc char[content.Length + 2]; - span[0] = span[span.Length - 1] = '"'; - content.AsSpan().CopyTo(span.Slice(1, content.Length)); - return span.ToString(); - } - - var contentWithQuotationMarks = new char[content.Length + 2]; - contentWithQuotationMarks[0] = contentWithQuotationMarks[contentWithQuotationMarks.Length - 1] = '"'; - // ReSharper restore UseIndexFromEndExpression - content.CopyTo(0, contentWithQuotationMarks, 1, content.Length); - return new string (contentWithQuotationMarks); + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + return hash; } /// - /// Appends the content of the collection with the specified header line to the string builder. - /// Each item is on a new line. + /// Creates a hash code from the six specified values. /// - /// The item type of the collection. - /// The string builder that the content is appended to. - /// The collection whose items will be appended to the string builder. - /// The string that will be placed before the actual items as a header. - /// The value indicating if a new line is added after the last item. This value defaults to true. - /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendCollectionContent([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string headerLine = "Content of the collection:", bool finishWithNewLine = true) => stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(headerLine).AppendItemsWithNewLine(items, finishWithNewLine: finishWithNewLine); - // ReSharper restore RedundantNullableFlowAttribute + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + return hash; + } + /// - /// Appends the string representations of the specified items to the string builder. + /// Creates a hash code from the seven specified values. /// - /// The string builder where the items will be appended to. - /// The items to be appended. - /// The characters used to separate the items. Defaults to ", " and is not appended after the last item. - /// The text that is appended to the string builder when is empty. Defaults to "empty collection". - /// Thrown when or is null. - [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendItems([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string itemSeparator = ", ", string emptyCollectionText = "empty collection") - // ReSharper restore RedundantNullableFlowAttribute + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7) { - stringBuilder.MustNotBeNull(nameof(stringBuilder)); - var list = items.MustNotBeNull(nameof(items)).AsList(); - var currentIndex = 0; - var itemsCount = list.Count; - if (itemsCount == 0) - { - return stringBuilder.Append(emptyCollectionText); - } - - while (true) - { - stringBuilder.Append(list[currentIndex].ToStringOrNull()); - if (currentIndex < itemsCount - 1) - { - stringBuilder.Append(itemSeparator); - } - else - { - return stringBuilder; - } - - ++currentIndex; - } + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + return hash; } /// - /// Appends the string representations of the specified items to the string builder. Each item is on its own line. + /// Creates a hash code from the eight specified values. /// - /// The string builder where the items will be appended to. - /// The items to be appended. - /// The text that is appended to the string builder when is empty. Defaults to "empty collection". - /// The value indicating if a new line is added after the last item. This value defaults to true. - /// Thrown when or is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("stringBuilder:null => halt; items:null => halt; stringBuilder:notnull => notnull")] - // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendItemsWithNewLine([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] IEnumerable items, string emptyCollectionText = "empty collection", bool finishWithNewLine = true) => stringBuilder.AppendItems(items, DefaultNewLineSeparator, emptyCollectionText).AppendLineIf(finishWithNewLine); - // ReSharper restore RedundantNullableFlowAttribute + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + return hash; + } + /// - /// Appends the value to the specified string builder if the condition is true. + /// Creates a hash code from the nine specified values. /// - /// The string builder where will be appended to. - /// The boolean value indicating whether the append operation will be performed or not. - /// The value to be appended to the string builder. - /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendIf(// ReSharper disable once RedundantNullableFlowAttribute - [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value) + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9) { - if (condition) - { - stringBuilder.MustNotBeNull(nameof(stringBuilder)).Append(value); - } - - return stringBuilder; + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + return hash; } /// - /// Appends the value followed by a new line separator to the specified string builder if the condition is true. + /// Creates a hash code from the ten specified values. /// - /// The string builder where will be appended to. - /// The boolean value indicating whether the append operation will be performed or not. - /// The value to be appended to the string builder (optional). This value defaults to an empty string. - /// Thrown when is null. [MethodImpl(MethodImplOptions.AggressiveInlining)] - [ContractAnnotation("stringBuilder:null => halt; stringBuilder:notnull => notnull")] - public static StringBuilder AppendLineIf(// ReSharper disable once RedundantNullableFlowAttribute - [NotNull][ValidatedNotNull] this StringBuilder stringBuilder, bool condition, string value = "") + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10) { - if (condition) - { - stringBuilder.MustNotBeNull(nameof(stringBuilder)).AppendLine(value); - } - - return stringBuilder; + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + return hash; } /// - /// Appends the messages of the and its nested exceptions to the - /// specified . + /// Creates a hash code from the eleven specified values. /// - /// Thrown when any parameter is null. - // ReSharper disable RedundantNullableFlowAttribute - public static StringBuilder AppendExceptionMessages([NotNull][ValidatedNotNull] this StringBuilder stringBuilder, [NotNull][ValidatedNotNull] Exception exception) - // ReSharper restore RedundantNullableFlowAttribute + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11) { - stringBuilder.MustNotBeNull(nameof(stringBuilder)); - exception.MustNotBeNull(nameof(exception)); - while (true) - { - // ReSharper disable once PossibleNullReferenceException - stringBuilder.AppendLine(exception.Message); - if (exception.InnerException is null) - { - return stringBuilder; - } - - stringBuilder.AppendLine(); - exception = exception.InnerException; - } + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + return hash; } /// - /// Formats all messages of the and its nested exceptions into - /// a single string. + /// Creates a hash code from the eleven specified values. /// - /// Thrown when is null. - // ReSharper disable once RedundantNullableFlowAttribute - public static string GetAllExceptionMessages([NotNull][ValidatedNotNull] this Exception exception) => new StringBuilder().AppendExceptionMessages(exception).ToString(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + CombineIntoHash(ref hash, value12); + return hash; + } + /// - /// Checks if the two strings are equal using ordinal sorting rules as well as ignoring the white space - /// of the provided strings. + /// Creates a hash code from the thirteen specified values. /// - public static bool EqualsOrdinalIgnoreWhiteSpace(this string? x, string? y) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13) { - if (ReferenceEquals(x, y)) - { - return true; - } - - if (x is null || y is null) - { - return false; - } - - if (x.Length == 0) - { - return y.Length == 0; - } - - var indexX = 0; - var indexY = 0; - bool wasXSuccessful; - bool wasYSuccessful; - // This condition of the while loop actually has to use the single '&' operator because - // y.TryAdvanceToNextNonWhiteSpaceCharacter must be called even though it already returned - // false on x. Otherwise, the 'wasXSuccessful == wasYSuccessful' comparison would not return - // the desired result. - while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) - { - if (x[indexX++] != y[indexY++]) - { - return false; - } - } - - return wasXSuccessful == wasYSuccessful; + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + CombineIntoHash(ref hash, value12); + CombineIntoHash(ref hash, value13); + return hash; } /// - /// Checks if the two strings are equal using ordinal sorting rules, ignoring the case of the letters - /// as well as ignoring the white space of the provided strings. + /// Creates a hash code from the fourteen specified values. /// - public static bool EqualsOrdinalIgnoreCaseIgnoreWhiteSpace(this string? x, string? y) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14) { - if (ReferenceEquals(x, y)) - { - return true; - } - - if (x is null || y is null) - { - return false; - } - - if (x.Length == 0) - { - return y.Length == 0; - } - - var indexX = 0; - var indexY = 0; - bool wasXSuccessful; - bool wasYSuccessful; - // This condition of the while loop actually has to use the single '&' operator because - // y.TryAdvanceToNextNonWhiteSpaceCharacter must be called even though it already returned - // false on x. Otherwise, the 'wasXSuccessful == wasYSuccessful' comparison would not return - // the desired result. - while ((wasXSuccessful = x.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexX)) & (wasYSuccessful = y.TryAdvanceToNextNonWhiteSpaceCharacter(ref indexY))) - { - if (char.ToLowerInvariant(x[indexX++]) != char.ToLowerInvariant(y[indexY++])) - { - return false; - } - } + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + CombineIntoHash(ref hash, value12); + CombineIntoHash(ref hash, value13); + CombineIntoHash(ref hash, value14); + return hash; + } - return wasXSuccessful == wasYSuccessful; + /// + /// Creates a hash code from the fifteen specified values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14, T15 value15) + { + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + CombineIntoHash(ref hash, value12); + CombineIntoHash(ref hash, value13); + CombineIntoHash(ref hash, value14); + CombineIntoHash(ref hash, value15); + return hash; } - private static bool TryAdvanceToNextNonWhiteSpaceCharacter(this string @string, ref int currentIndex) + /// + /// Creates a hash code from the sixteen specified values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CreateHashCode(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6, T7 value7, T8 value8, T9 value9, T10 value10, T11 value11, T12 value12, T13 value13, T14 value14, T15 value15, T16 value16) { - while (currentIndex < @string.Length) - { - if (!char.IsWhiteSpace(@string[currentIndex])) - { - return true; - } + var hash = FirstPrime; + CombineIntoHash(ref hash, value1); + CombineIntoHash(ref hash, value2); + CombineIntoHash(ref hash, value3); + CombineIntoHash(ref hash, value4); + CombineIntoHash(ref hash, value5); + CombineIntoHash(ref hash, value6); + CombineIntoHash(ref hash, value7); + CombineIntoHash(ref hash, value8); + CombineIntoHash(ref hash, value9); + CombineIntoHash(ref hash, value10); + CombineIntoHash(ref hash, value11); + CombineIntoHash(ref hash, value12); + CombineIntoHash(ref hash, value13); + CombineIntoHash(ref hash, value14); + CombineIntoHash(ref hash, value15); + CombineIntoHash(ref hash, value16); + return hash; + } - ++currentIndex; - } + /// + /// Mutates the given hash with the specified value using the following statement: + /// hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0);. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void CombineIntoHash(ref int hash, T value) => hash = unchecked(hash * SecondPrime + value?.GetHashCode() ?? 0); + } - return false; + /// + /// Represents a builder for the algorithm that does not allocate. + /// Should only be used in cases where the overload for sixteen values is not enough or a dedicated + /// initial hash must be provided (e.g. for test reasons). + /// Instantiate the builder with the method. You have to instantiate a new builder + /// for each hash code that you want to calculate. + /// + internal struct MultiplyAddHashBuilder + { + private int _hash; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private MultiplyAddHashBuilder(int initialHash) => _hash = initialHash; + /// + /// Combines the given value into the hash using the method. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MultiplyAddHashBuilder CombineIntoHash(T value) + { + MultiplyAddHash.CombineIntoHash(ref _hash, value); + return this; } + + /// + /// Returns the calculated hash code. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int BuildHash() => _hash; + /// + /// Initializes a new instance of with the specified initial hash. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static MultiplyAddHashBuilder Create(int initialHash = MultiplyAddHash.FirstPrime) => new(initialHash); } }