diff --git a/.editorconfig b/.editorconfig
index 175c36f..fa80a79 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -44,6 +44,7 @@ generated_code = true
# XML project files
[*.{slnx,csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,nativeproj,locproj}]
indent_size = 2
+max_line_length = 160
# Xml build files
[*.builds]
diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml
index cead3c1..62d43f8 100644
--- a/.github/workflows/cicd.yml
+++ b/.github/workflows/cicd.yml
@@ -25,5 +25,6 @@ jobs:
with:
dotnetLogging: ${{ inputs.dotnet-logging }}
dotnetVersion: ${{ vars.NE_DOTNET_TARGETFRAMEWORKS }}
+ runsOnBuild: windows-latest
solution: ./Arguments.slnx
secrets: inherit
diff --git a/Directory.Build.props b/Directory.Build.props
index 2fb19b4..f38854d 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -1,6 +1,15 @@
-
- net8.0
+ <_NetTargetFrameworks>net6.0;net7.0;net8.0;net9.0;net10.0
+ <_ClassicTargetFrameworks>net472;net48;net481
+ <_ProjectTargetFrameworks>$(_NetTargetFrameworks);netstandard2.0
+ <_ProjectTargetFrameworks Condition=" '$(OS)' == 'Windows_NT' "
+ >$(_ProjectTargetFrameworks);$(_ClassicTargetFrameworks)
+ <_TestTargetFrameworks>$(_NetTargetFrameworks)
+ <_TestTargetFrameworks Condition=" '$(OS)' == 'Windows_NT' "
+ >$(_TestTargetFrameworks);$(_ClassicTargetFrameworks)
+ false
diff --git a/README.md b/README.md
index ee08d0f..2d515c8 100644
--- a/README.md
+++ b/README.md
@@ -9,11 +9,21 @@ A comprehensive library providing backward-compatible argument validation helper
Modern .NET versions (starting with .NET 6) introduced streamlined argument validation methods such as `ArgumentNullException.ThrowIfNull` and `ArgumentOutOfRangeException.ThrowIfEqual`. However, projects targeting multiple frameworks or older .NET versions cannot utilize these convenient methods without conditional compilation or duplicated validation logic.
-**NetEvolve.Arguments** bridges this gap by providing polyfilled implementations of these modern validation methods, allowing developers to write consistent, maintainable argument validation code regardless of the target framework.
+**NetEvolve.Arguments** bridges this gap by providing full polyfill implementations via extension methods on `ArgumentNullException`, `ArgumentException`, and `ArgumentOutOfRangeException`. These polyfills enable the use of modern .NET API patterns across all supported frameworks, allowing developers to write consistent, maintainable argument validation code regardless of the target framework.
+
+### Polyfill Architecture
+
+The library provides polyfills through three main extension classes:
+
+- **`ArgumentNullExceptionPolyfills`**: Extends `ArgumentNullException` with `ThrowIfNull` methods
+- **`ArgumentExceptionPolyfills`**: Extends `ArgumentException` with `ThrowIfNullOrEmpty` and `ThrowIfNullOrWhiteSpace` methods
+- **`ArgumentOutOfRangeExceptionPolyfills`**: Extends `ArgumentOutOfRangeException` with range validation methods (`ThrowIfZero`, `ThrowIfNegative`, `ThrowIfEqual`, comparison methods, etc.)
+
+These polyfills are conditionally compiled and only active when targeting frameworks that don't provide the native implementations, ensuring zero overhead on modern .NET versions.
## Key Features
-- **Multi-Framework Support**: Compatible with .NET Standard 2.0, .NET 8.0, .NET 9.0, and .NET 10.0
+- **Multi-Framework Support**: Compatible with .NET Standard 2.0, .NET 6.0-10.0, and .NET Framework 4.7.2-4.8.1 (on Windows)
- **Zero Runtime Overhead**: Uses conditional compilation to delegate to native implementations where available
- **Drop-in Replacement**: Identical API signatures to native .NET implementations
- **Type-Safe**: Fully generic implementations with proper type constraints
@@ -35,154 +45,233 @@ Install-Package NetEvolve.Arguments
## Usage
-Import the namespace in your code:
-
-```csharp
-using NetEvolve.Arguments;
-```
-
-Then use the validation methods just as you would with native .NET implementations:
+Simply use the validation methods directly on the exception types, just as you would with native .NET 8+ implementations:
```csharp
public void ProcessData(string data, int count)
{
- Argument.ThrowIfNullOrWhiteSpace(data);
- Argument.ThrowIfLessThan(count, 1);
+ ArgumentException.ThrowIfNullOrWhiteSpace(data);
+ ArgumentOutOfRangeException.ThrowIfLessThan(count, 1);
// Your implementation
}
```
+The polyfills are automatically available through extension methods when targeting older frameworks. No additional using directives are needed since the polyfills reside in the `System` namespace.
+
## Available Methods
### Null Validation
-#### `Argument.ThrowIfNull(object?, string?)`
+#### `ArgumentNullException.ThrowIfNull(object?, string?)`
Throws an `ArgumentNullException` if the argument is `null`.
-**Replacement for**: [`ArgumentNullException.ThrowIfNull(object, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentnullexception.throwifnull) (introduced in .NET 6)
+**Native API**: [`ArgumentNullException.ThrowIfNull`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentnullexception.throwifnull) (introduced in .NET 6)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1
**Example**:
```csharp
public void Process(object data)
{
- Argument.ThrowIfNull(data);
+ ArgumentNullException.ThrowIfNull(data);
}
```
-#### `Argument.ThrowIfNull(void*, string?)`
+#### `ArgumentNullException.ThrowIfNull(void*, string?)`
Throws an `ArgumentNullException` if the pointer argument is `null`.
-**Replacement for**: [`ArgumentNullException.ThrowIfNull(void*, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentnullexception.throwifnull?view=net-8.0#system-argumentnullexception-throwifnull(system-void*-system-string)) (introduced in .NET 7)
+**Native API**: [`ArgumentNullException.ThrowIfNull(void*)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentnullexception.throwifnull?view=net-10.0#system-argumentnullexception-throwifnull(system-void*-system-string)) (introduced in .NET 7)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0
+
+**Example**:
+```csharp
+public unsafe void Process(void* pointer)
+{
+ ArgumentNullException.ThrowIfNull(pointer);
+}
+```
-#### `Argument.ThrowIfNullOrEmpty(string?, string?)`
+#### `ArgumentException.ThrowIfNullOrEmpty(string?, string?)`
Throws an `ArgumentNullException` if the argument is `null`, or an `ArgumentException` if the argument is an empty string.
-**Replacement for**: [`ArgumentException.ThrowIfNullOrEmpty(string, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception.throwifnullorempty) (introduced in .NET 7)
+**Native API**: [`ArgumentException.ThrowIfNullOrEmpty`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception.throwifnullorempty) (introduced in .NET 7)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0
**Example**:
```csharp
public void Process(string name)
{
- Argument.ThrowIfNullOrEmpty(name);
+ ArgumentException.ThrowIfNullOrEmpty(name);
}
```
-#### `Argument.ThrowIfNullOrEmpty(IEnumerable?, string?)`
+#### `ArgumentException.ThrowIfNullOrEmpty(IEnumerable?, string?)`
Throws an `ArgumentNullException` if the argument is `null`, or an `ArgumentException` if the collection is empty.
-**Note**: This is a custom extension method not present in the base .NET framework, providing convenient collection validation.
+**Note**: This is a custom extension method not present in the native .NET framework, providing convenient collection validation.
+
+**Availability**: All supported frameworks
**Example**:
```csharp
public void Process(IEnumerable items)
{
- Argument.ThrowIfNullOrEmpty(items);
+ ArgumentException.ThrowIfNullOrEmpty(items);
}
```
-#### `Argument.ThrowIfNullOrWhiteSpace(string?, string?)`
+#### `ArgumentException.ThrowIfNullOrWhiteSpace(string?, string?)`
Throws an `ArgumentNullException` if the argument is `null`, or an `ArgumentException` if the argument is empty or contains only white-space characters.
-**Replacement for**: [`ArgumentException.ThrowIfNullOrWhiteSpace(string, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception.throwifnullorwhitespace) (introduced in .NET 8)
+**Native API**: [`ArgumentException.ThrowIfNullOrWhiteSpace`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception.throwifnullorwhitespace) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
**Example**:
```csharp
public void Process(string description)
{
- Argument.ThrowIfNullOrWhiteSpace(description);
+ ArgumentException.ThrowIfNullOrWhiteSpace(description);
}
```
### Range Validation
-#### `Argument.ThrowIfEqual(T, T, string?)`
+#### `ArgumentOutOfRangeException.ThrowIfZero(T, string?)`
+Throws an `ArgumentOutOfRangeException` if the argument is zero.
+
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfZero`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifzero) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
+
+**Example**:
+```csharp
+public void SetDivisor(int divisor)
+{
+ ArgumentOutOfRangeException.ThrowIfZero(divisor);
+}
+```
+
+#### `ArgumentOutOfRangeException.ThrowIfNegative(T, string?)`
+Throws an `ArgumentOutOfRangeException` if the argument is negative.
+
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfNegative`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifnegative) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
+
+**Example**:
+```csharp
+public void SetCount(int count)
+{
+ ArgumentOutOfRangeException.ThrowIfNegative(count);
+}
+```
+
+#### `ArgumentOutOfRangeException.ThrowIfNegativeOrZero(T, string?)`
+Throws an `ArgumentOutOfRangeException` if the argument is negative or zero.
+
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfNegativeOrZero`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifnegativeorzero) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
+
+**Example**:
+```csharp
+public void SetQuantity(int quantity)
+{
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(quantity);
+}
+```
+
+#### `ArgumentOutOfRangeException.ThrowIfEqual(T, T, string?)`
Throws an `ArgumentOutOfRangeException` if the first argument is equal to the second argument.
-**Replacement for**: [`ArgumentOutOfRangeException.ThrowIfEqual(T, T, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifequal) (introduced in .NET 8)
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfEqual`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifequal) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
**Example**:
```csharp
public void SetValue(int value)
{
- Argument.ThrowIfEqual(value, 0); // Value must not be zero
+ ArgumentOutOfRangeException.ThrowIfEqual(value, 0); // Value must not be zero
}
```
-#### `Argument.ThrowIfNotEqual(T, T, string?)`
+#### `ArgumentOutOfRangeException.ThrowIfNotEqual(T, T, string?)`
Throws an `ArgumentOutOfRangeException` if the first argument is not equal to the second argument.
-**Replacement for**: [`ArgumentOutOfRangeException.ThrowIfNotEqual(T, T, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifnotequal) (introduced in .NET 8)
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfNotEqual`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifnotequal) (introduced in .NET 8)
-#### `Argument.ThrowIfGreaterThan(T, T, string?)`
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
+
+**Example**:
+```csharp
+public void ValidateConstant(int value)
+{
+ ArgumentOutOfRangeException.ThrowIfNotEqual(value, 42); // Value must be exactly 42
+}
+```
+
+#### `ArgumentOutOfRangeException.ThrowIfGreaterThan(T, T, string?)`
Throws an `ArgumentOutOfRangeException` if the first argument is greater than the second argument.
-**Replacement for**: [`ArgumentOutOfRangeException.ThrowIfGreaterThan(T, T, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifgreaterthan) (introduced in .NET 8)
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfGreaterThan`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifgreaterthan) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
**Example**:
```csharp
public void SetAge(int age)
{
- Argument.ThrowIfGreaterThan(age, 150); // Age must be 150 or less
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(age, 150); // Age must be 150 or less
}
```
-#### `Argument.ThrowIfGreaterThanOrEqual(T, T, string?)`
+#### `ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(T, T, string?)`
Throws an `ArgumentOutOfRangeException` if the first argument is greater than or equal to the second argument.
-**Replacement for**: [`ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(T, T, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifgreaterthanorequal) (introduced in .NET 8)
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwifgreaterthanorequal) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
**Example**:
```csharp
public void SetCount(int count, int maximum)
{
- Argument.ThrowIfGreaterThanOrEqual(count, maximum); // Count must be less than maximum
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(count, maximum); // Count must be less than maximum
}
```
-#### `Argument.ThrowIfLessThan(T, T, string?)`
+#### `ArgumentOutOfRangeException.ThrowIfLessThan(T, T, string?)`
Throws an `ArgumentOutOfRangeException` if the first argument is less than the second argument.
-**Replacement for**: [`ArgumentOutOfRangeException.ThrowIfLessThan(T, T, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwiflessthan) (introduced in .NET 8)
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfLessThan`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwiflessthan) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
**Example**:
```csharp
public void SetCount(int count)
{
- Argument.ThrowIfLessThan(count, 1); // Count must be at least 1
+ ArgumentOutOfRangeException.ThrowIfLessThan(count, 1); // Count must be at least 1
}
```
-#### `Argument.ThrowIfLessThanOrEqual(T, T, string?)`
+#### `ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(T, T, string?)`
Throws an `ArgumentOutOfRangeException` if the first argument is less than or equal to the second argument.
-**Replacement for**: [`ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(T, T, string)`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwiflessthanorequal) (introduced in .NET 8)
+**Native API**: [`ArgumentOutOfRangeException.ThrowIfLessThanOrEqual`](https://learn.microsoft.com/en-us/dotnet/api/system.argumentoutofrangeexception.throwiflessthanorequal) (introduced in .NET 8)
+
+**Polyfill availability**: .NET Standard 2.0, .NET Framework 4.7.2-4.8.1, .NET 6.0, .NET 7.0
**Example**:
```csharp
public void SetMinimum(int value, int threshold)
{
- Argument.ThrowIfLessThanOrEqual(value, threshold); // Value must be greater than threshold
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, threshold); // Value must be greater than threshold
}
```
@@ -191,6 +280,11 @@ public void SetMinimum(int value, int threshold)
| Target Framework | Status | Notes |
|-----------------|--------|-------|
| .NET Standard 2.0 | ✅ Supported | Full polyfill implementations |
+| .NET Framework 4.7.2 | ✅ Supported | Windows only, full polyfill implementations |
+| .NET Framework 4.8 | ✅ Supported | Windows only, full polyfill implementations |
+| .NET Framework 4.8.1 | ✅ Supported | Windows only, full polyfill implementations |
+| .NET 6.0 | ✅ Supported | Delegates to native implementations where available |
+| .NET 7.0 | ✅ Supported | Delegates to native implementations where available |
| .NET 8.0 | ✅ Supported | Delegates to native implementations where available |
| .NET 9.0 | ✅ Supported | Full native delegation |
| .NET 10.0 | ✅ Supported | Full native delegation |
diff --git a/src/NetEvolve.Arguments/Argument.cs b/src/NetEvolve.Arguments/Argument.cs
index 3ef639d..c9916d9 100644
--- a/src/NetEvolve.Arguments/Argument.cs
+++ b/src/NetEvolve.Arguments/Argument.cs
@@ -5,5 +5,5 @@
///
/// Provides a set of backward compatible `throw` helper methods, which have been added in previous .NET versions.
///
-[SuppressMessage("Style", "IDE0022:Use expression body for method", Justification = "As designed.")]
-public static partial class Argument { }
+[SuppressMessage("Info Code Smell", "S1133:Deprecated code should be removed", Justification = "As designed.")]
+public static partial class Argument;
diff --git a/src/NetEvolve.Arguments/ArgumentExceptionPolyfill.cs b/src/NetEvolve.Arguments/ArgumentExceptionPolyfill.cs
new file mode 100644
index 0000000..5b9a19c
--- /dev/null
+++ b/src/NetEvolve.Arguments/ArgumentExceptionPolyfill.cs
@@ -0,0 +1,86 @@
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+/// Provides polyfill extension methods for to support newer .NET APIs in older framework versions.
+[SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "As designed.")]
+public static class ArgumentExceptionPolyfills
+{
+ extension(ArgumentException)
+ {
+#if !NET8_0_OR_GREATER
+ /// Throws an exception if is or empty.
+ /// The string argument to validate as non- and non-empty.
+ /// The name of the parameter with which corresponds.
+ /// is .
+ /// is empty.
+ ///
+ public static void ThrowIfNullOrEmpty(
+ [NotNull] string? argument,
+ [CallerArgumentExpression(nameof(argument))] string? paramName = null
+ )
+ {
+ if (argument is null)
+ {
+ throw new ArgumentNullException(paramName);
+ }
+
+ if (argument.Length == 0)
+ {
+ throw new ArgumentException("The value cannot be an empty string.", paramName);
+ }
+ }
+
+ /// Throws an exception if is , empty, or consists only of white-space characters.
+ /// The string argument to validate.
+ /// The name of the parameter with which corresponds.
+ /// is .
+ /// is empty or consists only of white-space characters.
+ ///
+ public static void ThrowIfNullOrWhiteSpace(
+ [NotNull] string? argument,
+ [CallerArgumentExpression(nameof(argument))] string? paramName = null
+ )
+ {
+ if (argument is null)
+ {
+ throw new ArgumentNullException(paramName);
+ }
+
+ if (string.IsNullOrWhiteSpace(argument))
+ {
+ throw new ArgumentException(
+ "The value cannot be an empty string or composed entirely of whitespace.",
+ paramName
+ );
+ }
+ }
+#endif
+
+ /// Throws an exception if is or empty.
+ /// The type of objects to enumerate.
+ /// The enumerable argument to validate as non- and non-empty.
+ /// The name of the parameter with which corresponds.
+ /// is .
+ /// is empty.
+ public static void ThrowIfNullOrEmpty(
+ [NotNull] IEnumerable? argument,
+ [CallerArgumentExpression(nameof(argument))] string? paramName = null
+ )
+ {
+ if (argument is null)
+ {
+ throw new ArgumentNullException(paramName);
+ }
+
+ if (argument.TryGetNonEnumeratedCount(out var count) && count == 0)
+ {
+ throw new ArgumentException("The collection cannot be empty.", paramName);
+ }
+ }
+ }
+}
diff --git a/src/NetEvolve.Arguments/ArgumentNullExceptionPolyfills.cs b/src/NetEvolve.Arguments/ArgumentNullExceptionPolyfills.cs
new file mode 100644
index 0000000..439c678
--- /dev/null
+++ b/src/NetEvolve.Arguments/ArgumentNullExceptionPolyfills.cs
@@ -0,0 +1,53 @@
+#if !NET9_0_OR_GREATER
+
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
+
+/// Provides polyfill extension methods for to support newer .NET APIs in older framework versions.
+[SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "As designed.")]
+public static class ArgumentNullExceptionPolyfills
+{
+ extension(ArgumentNullException)
+ {
+#if !NET6_0_OR_GREATER
+ /// Throws an if is .
+ /// The reference type argument to validate as non-null.
+ /// The name of the parameter with which corresponds. If you omit this parameter, the name of is used.
+ /// is .
+ /// The parameter is included to support the attribute. It's recommended that you don't pass a value for this parameter and let the name of be used instead.
+ ///
+ public static void ThrowIfNull(
+ [NotNull] object? argument,
+ [CallerArgumentExpression(nameof(argument))] string? paramName = null
+ )
+ {
+ if (argument is null)
+ {
+ throw new ArgumentNullException(paramName);
+ }
+ }
+#endif
+
+ /// Throws an if is .
+ /// The pointer argument to validate as non-null.
+ /// The name of the parameter with which corresponds.
+ /// is .
+ ///
+ public static unsafe void ThrowIfNull(
+ void* argument,
+ [CallerArgumentExpression(nameof(argument))] string? paramName = null
+ )
+ {
+ if (argument is null)
+ {
+ throw new ArgumentNullException(paramName);
+ }
+ }
+ }
+}
+#endif
diff --git a/src/NetEvolve.Arguments/ArgumentOutOfRangeExceptionPolyfills.cs b/src/NetEvolve.Arguments/ArgumentOutOfRangeExceptionPolyfills.cs
new file mode 100644
index 0000000..fca9d8a
--- /dev/null
+++ b/src/NetEvolve.Arguments/ArgumentOutOfRangeExceptionPolyfills.cs
@@ -0,0 +1,335 @@
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+using System.Diagnostics.CodeAnalysis;
+
+/// Provides polyfill extension methods for to support newer .NET APIs in older framework versions.
+[SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "As designed.")]
+public static class ArgumentOutOfRangeExceptionPolyfills
+{
+ extension(ArgumentOutOfRangeException)
+ {
+#if !NET8_0_OR_GREATER
+ /// Throws an if is zero.
+ /// The type of the object to validate.
+ /// The argument to validate as non-zero.
+ /// The name of the parameter with which corresponds.
+ /// is zero.
+ ///
+ public static void ThrowIfZero(
+ T value,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+#if NET7_0_OR_GREATER
+ where T : Numerics.INumberBase
+ {
+ if (T.IsZero(value))
+ {
+ ThrowZero(paramName);
+ }
+ }
+#else
+ where T : struct, IEquatable
+ {
+ if (value.Equals(default))
+ {
+ ThrowZero(paramName);
+ }
+ }
+#endif
+
+ /// Throws an with a message indicating that a value must not be zero.
+ /// The name of the parameter with which the value corresponds.
+ /// Always thrown.
+ ///
+ ///
+ /// This is a helper method used internally by
+ /// to throw an exception when a zero value is detected. The method is marked with
+ /// to inform the compiler that it unconditionally throws.
+ ///
+ ///
+ [DoesNotReturn]
+ private static void ThrowZero(string? paramName) =>
+ throw new ArgumentOutOfRangeException(paramName, "Value must not be zero.");
+#endif
+
+#if !NET8_0_OR_GREATER
+ /// Throws an if is negative.
+ /// The type of the object to validate.
+ /// The argument to validate as non-negative.
+ /// The name of the parameter with which corresponds.
+ /// is negative.
+ ///
+ public static void ThrowIfNegative(
+ T value,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+#if NET7_0_OR_GREATER
+ where T : Numerics.INumberBase
+ {
+ if (T.IsNegative(value))
+ {
+ ThrowNegative(value, paramName);
+ }
+ }
+#else
+ where T : struct, IComparable
+ {
+ if (value.CompareTo(default) < 0)
+ {
+ ThrowNegative(value, paramName);
+ }
+ }
+#endif
+
+#if !NET7_0_OR_GREATER
+ /// Throws an if is negative.
+ /// The argument to validate as non-negative.
+ /// The name of the parameter with which corresponds.
+ /// is negative.
+ ///
+ public static void ThrowIfNegative(
+ nint value,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+ {
+ if (value < (nint)0)
+ {
+ ThrowNegative(value, paramName);
+ }
+ }
+#endif
+
+ /// Throws an with a message indicating that a value must be non-negative.
+ /// The type of the value being validated.
+ /// The value that failed validation.
+ /// The name of the parameter with which corresponds.
+ /// Always thrown.
+ ///
+ ///
+ /// This is a helper method used internally by
+ /// overloads to throw an exception when a negative value is detected. The method is marked with
+ /// to inform the compiler that it unconditionally throws.
+ ///
+ ///
+ [DoesNotReturn]
+ private static void ThrowNegative(T value, string? paramName) =>
+ throw new ArgumentOutOfRangeException(paramName, value, "Value must be non-negative.");
+
+#endif
+
+#if !NET8_0_OR_GREATER
+ /// Throws an if is negative or zero.
+ /// The type of the object to validate.
+ /// The argument to validate as non-zero and non-negative.
+ /// The name of the parameter with which corresponds.
+ /// is negative or zero.
+ ///
+ public static void ThrowIfNegativeOrZero(
+ T value,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+#if NET7_0_OR_GREATER
+ where T : Numerics.INumberBase
+ {
+ if (T.IsNegative(value) || T.IsZero(value))
+ {
+ ThrowNegativeOrZero(value, paramName);
+ }
+ }
+#else
+ where T : struct, IComparable
+ {
+ if (value.CompareTo(default) <= 0)
+ {
+ ThrowNegativeOrZero(value, paramName);
+ }
+ }
+#endif
+#endif
+
+#if !NET7_0_OR_GREATER
+ /// Throws an if is negative or zero.
+ /// The argument to validate as non-zero and non-negative.
+ /// The name of the parameter with which corresponds.
+ /// is negative or zero.
+ ///
+ public static void ThrowIfNegativeOrZero(
+ nint value,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+ {
+ if (value <= (nint)0)
+ {
+ ThrowNegativeOrZero(value, paramName);
+ }
+ }
+#endif
+
+ /// Throws an with a message indicating that a value must be non-negative and non-zero.
+ /// The type of the value being validated.
+ /// The value that failed validation.
+ /// The name of the parameter with which corresponds.
+ /// Always thrown.
+ ///
+ ///
+ /// This is a helper method used internally by the
+ /// overloads to throw an exception when a negative or zero value is detected. The method is marked with
+ /// to inform the compiler that it unconditionally throws.
+ ///
+ ///
+ /// The exception message includes the parameter name and the actual value that failed validation
+ /// to provide detailed diagnostic information.
+ ///
+ ///
+ [DoesNotReturn]
+ private static void ThrowNegativeOrZero(T value, string? name) =>
+ throw new ArgumentOutOfRangeException(
+ name,
+ value,
+ $"{name} ('{value}') must be a non-negative and non-zero value."
+ );
+
+#if !NET8_0_OR_GREATER
+ /// Throws an if is equal to .
+ /// The type of the objects to validate.
+ /// The argument to validate as not equal to .
+ /// The value to compare with .
+ /// The name of the parameter with which corresponds.
+ /// is equal to .
+ ///
+ public static void ThrowIfEqual(
+ T value,
+ T other,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+ where T : IEquatable?
+ {
+ if (EqualityComparer.Default.Equals(value, other))
+ {
+ throw new ArgumentOutOfRangeException(paramName, value, $"Value must not be equal to {other}.");
+ }
+ }
+#endif
+
+#if !NET8_0_OR_GREATER
+ /// Throws an if is not equal to .
+ /// The type of the objects to validate.
+ /// The argument to validate as equal to .
+ /// The value to compare with .
+ /// The name of the parameter with which corresponds.
+ /// is not equal to .
+ ///
+ public static void ThrowIfNotEqual(
+ T value,
+ T other,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+ where T : IEquatable?
+ {
+ if (!EqualityComparer.Default.Equals(value, other))
+ {
+ throw new ArgumentOutOfRangeException(paramName, value, $"Value must be equal to {other}.");
+ }
+ }
+#endif
+
+#if !NET8_0_OR_GREATER
+ /// Throws an if is greater than .
+ /// The type of the objects to compare.
+ /// The argument to validate as less than or equal to .
+ /// The value to compare with .
+ /// The name of the parameter with which corresponds.
+ /// is greater than .
+ ///
+ public static void ThrowIfGreaterThan(
+ T value,
+ T other,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+ where T : IComparable
+ {
+ if (value.CompareTo(other) > 0)
+ {
+ throw new ArgumentOutOfRangeException(
+ paramName,
+ value,
+ $"Value must be less than or equal to {other}."
+ );
+ }
+ }
+#endif
+
+#if !NET8_0_OR_GREATER
+ /// Throws an if is greater than or equal to .
+ /// The type of the objects to compare.
+ /// The argument to validate as less than .
+ /// The value to compare with .
+ /// The name of the parameter with which corresponds.
+ /// is greater than or equal to .
+ ///
+ public static void ThrowIfGreaterThanOrEqual(
+ T value,
+ T other,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+ where T : IComparable
+ {
+ if (value.CompareTo(other) >= 0)
+ {
+ throw new ArgumentOutOfRangeException(paramName, value, $"Value must be less than {other}.");
+ }
+ }
+#endif
+
+#if !NET8_0_OR_GREATER
+ /// Throws an if is less than .
+ /// The type of the objects to compare.
+ /// The argument to validate as greater than or equal to .
+ /// The value to compare with .
+ /// The name of the parameter with which corresponds.
+ /// is less than .
+ ///
+ public static void ThrowIfLessThan(
+ T value,
+ T other,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+ where T : IComparable
+ {
+ if (value.CompareTo(other) < 0)
+ {
+ throw new ArgumentOutOfRangeException(
+ paramName,
+ value,
+ $"Value must be greater than or equal to {other}."
+ );
+ }
+ }
+#endif
+
+#if !NET8_0_OR_GREATER
+ /// Throws an if is less than or equal to .
+ /// The type of the objects to compare.
+ /// The argument to validate as greater than .
+ /// The value to compare with .
+ /// The name of the parameter with which corresponds.
+ /// is less than or equal to .
+ ///
+ public static void ThrowIfLessThanOrEqual(
+ T value,
+ T other,
+ [Runtime.CompilerServices.CallerArgumentExpression(nameof(value))] string? paramName = null
+ )
+ where T : IComparable
+ {
+ if (value.CompareTo(other) <= 0)
+ {
+ throw new ArgumentOutOfRangeException(paramName, value, $"Value must be greater than {other}.");
+ }
+ }
+#endif
+ }
+}
diff --git a/src/NetEvolve.Arguments/Argument_EMPTY.cs b/src/NetEvolve.Arguments/Argument_EMPTY.cs
deleted file mode 100644
index 3f5175b..0000000
--- a/src/NetEvolve.Arguments/Argument_EMPTY.cs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace NetEvolve.Arguments;
-
-public static partial class Argument { }
diff --git a/src/NetEvolve.Arguments/Argument_ThrowArgumentException.cs b/src/NetEvolve.Arguments/Argument_ThrowArgumentException.cs
deleted file mode 100644
index d146d78..0000000
--- a/src/NetEvolve.Arguments/Argument_ThrowArgumentException.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace NetEvolve.Arguments;
-
-using System;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-
-public static partial class Argument
-{
- /// Throws an .
- [DoesNotReturn]
- [MethodImpl(MethodImplOptions.NoInlining)]
- [StackTraceHidden]
- private static void ThrowArgumentException(string? paramName, string? message = null)
- {
- throw new ArgumentException(message, paramName);
- }
-}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowArgumentNullException.cs b/src/NetEvolve.Arguments/Argument_ThrowArgumentNullException.cs
deleted file mode 100644
index 51e959d..0000000
--- a/src/NetEvolve.Arguments/Argument_ThrowArgumentNullException.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace NetEvolve.Arguments;
-
-using System;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-
-public static partial class Argument
-{
- /// Throws an .
- [DoesNotReturn]
- [MethodImpl(MethodImplOptions.NoInlining)]
- [StackTraceHidden]
- private static void ThrowArgumentNullException(string? paramName, string? message = null)
- {
- throw new ArgumentNullException(paramName, message);
- }
-}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowArgumentOutOfRangeException.cs b/src/NetEvolve.Arguments/Argument_ThrowArgumentOutOfRangeException.cs
deleted file mode 100644
index 40cab8d..0000000
--- a/src/NetEvolve.Arguments/Argument_ThrowArgumentOutOfRangeException.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-#if !NET8_0_OR_GREATER
-namespace NetEvolve.Arguments;
-
-using System;
-using System.Diagnostics;
-using System.Diagnostics.CodeAnalysis;
-using System.Runtime.CompilerServices;
-
-public static partial class Argument
-{
- /// Throws an .
- [DoesNotReturn]
- [MethodImpl(MethodImplOptions.NoInlining)]
- [StackTraceHidden]
- private static void ThrowArgumentOutOfRangeException(string? paramName, T value, string? message = null)
- {
- throw new ArgumentOutOfRangeException(paramName, value, message);
- }
-}
-#endif
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfEqual.cs b/src/NetEvolve.Arguments/Argument_ThrowIfEqual.cs
index dc41779..18324ed 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfEqual.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfEqual.cs
@@ -1,7 +1,6 @@
namespace NetEvolve.Arguments;
using System;
-using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
@@ -11,31 +10,14 @@ public static partial class Argument
/// The argument to validate as not equal to .
/// The value to compare with .
/// The name of the parameter with which corresponds.
+ [Obsolete("Use ArgumentOutOfRangeException.ThrowIfEqual instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET8_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfEqual(
T value,
T other,
[CallerArgumentExpression(nameof(value))] string? paramName = null
)
- where T : IEquatable
- {
-#if NET8_0_OR_GREATER
- ArgumentOutOfRangeException.ThrowIfEqual(value, other, paramName);
-#else
- if (EqualityComparer.Default.Equals(value, other))
- {
- ThrowArgumentOutOfRangeException(
- paramName,
- value,
- $"{paramName} ('{value}') must not be equal to '{other}'."
- );
- }
-#endif
- }
+ where T : IEquatable => ArgumentOutOfRangeException.ThrowIfEqual(value, other, paramName);
}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfGreaterThan.cs b/src/NetEvolve.Arguments/Argument_ThrowIfGreaterThan.cs
index d939073..9e4fb0e 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfGreaterThan.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfGreaterThan.cs
@@ -10,31 +10,14 @@ public static partial class Argument
/// The argument to validate as less or equal than .
/// The value to compare with .
/// The name of the parameter with which corresponds.
+ [Obsolete("Use ArgumentOutOfRangeException.ThrowIfGreaterThan instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET8_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfGreaterThan(
T value,
T other,
[CallerArgumentExpression(nameof(value))] string? paramName = null
)
- where T : IComparable
- {
-#if NET8_0_OR_GREATER
- ArgumentOutOfRangeException.ThrowIfGreaterThan(value, other, paramName);
-#else
- if (value.CompareTo(other) > 0)
- {
- ThrowArgumentOutOfRangeException(
- paramName,
- value,
- $"{paramName} ('{value}') must be less than or equal to '{other}'."
- );
- }
-#endif
- }
+ where T : IComparable => ArgumentOutOfRangeException.ThrowIfGreaterThan(value, other, paramName);
}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfGreaterThanOrEqual.cs b/src/NetEvolve.Arguments/Argument_ThrowIfGreaterThanOrEqual.cs
index eac31a6..4357800 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfGreaterThanOrEqual.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfGreaterThanOrEqual.cs
@@ -10,27 +10,14 @@ public static partial class Argument
/// The argument to validate as less than .
/// The value to compare with .
/// The name of the parameter with which corresponds.
+ [Obsolete("Use ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET8_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfGreaterThanOrEqual(
T value,
T other,
[CallerArgumentExpression(nameof(value))] string? paramName = null
)
- where T : IComparable
- {
-#if NET8_0_OR_GREATER
- ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(value, other, paramName);
-#else
- if (value.CompareTo(other) >= 0)
- {
- ThrowArgumentOutOfRangeException(paramName, value, $"{paramName} ('{value}') must be less than '{other}'.");
- }
-#endif
- }
+ where T : IComparable => ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(value, other, paramName);
}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfLessThan.cs b/src/NetEvolve.Arguments/Argument_ThrowIfLessThan.cs
index a263962..cd0bf57 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfLessThan.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfLessThan.cs
@@ -10,31 +10,14 @@ public static partial class Argument
/// The argument to validate as greatar than or equal than .
/// The value to compare with .
/// The name of the parameter with which corresponds.
+ [Obsolete("Use ArgumentOutOfRangeException.ThrowIfLessThan instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET8_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfLessThan(
T value,
T other,
[CallerArgumentExpression(nameof(value))] string? paramName = null
)
- where T : IComparable
- {
-#if NET8_0_OR_GREATER
- ArgumentOutOfRangeException.ThrowIfLessThan(value, other, paramName);
-#else
- if (value.CompareTo(other) < 0)
- {
- ThrowArgumentOutOfRangeException(
- paramName,
- value,
- $"{paramName} ('{value}') must be greater than '{other}'."
- );
- }
-#endif
- }
+ where T : IComparable => ArgumentOutOfRangeException.ThrowIfLessThan(value, other, paramName);
}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfLessThanOrEqual.cs b/src/NetEvolve.Arguments/Argument_ThrowIfLessThanOrEqual.cs
index 9db4f09..adc0c08 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfLessThanOrEqual.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfLessThanOrEqual.cs
@@ -7,34 +7,17 @@
public static partial class Argument
{
/// Throws an if is less than or equal .
- /// The argument to validate as greatar than than .
+ /// The argument to validate as greatar than .
/// The value to compare with .
/// The name of the parameter with which corresponds.
+ [Obsolete("Use ArgumentOutOfRangeException.ThrowIfLessThanOrEqual instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET8_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfLessThanOrEqual(
T value,
T other,
[CallerArgumentExpression(nameof(value))] string? paramName = null
)
- where T : IComparable
- {
-#if NET8_0_OR_GREATER
- ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, other, paramName);
-#else
- if (value.CompareTo(other) <= 0)
- {
- ThrowArgumentOutOfRangeException(
- paramName,
- value,
- $"{paramName} ('{value}') must be greater than or equal to '{other}'."
- );
- }
-#endif
- }
+ where T : IComparable => ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, other, paramName);
}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfNotEqual.cs b/src/NetEvolve.Arguments/Argument_ThrowIfNotEqual.cs
index dd36012..d9e995a 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfNotEqual.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfNotEqual.cs
@@ -11,27 +11,14 @@ public static partial class Argument
/// The argument to validate as equal to .
/// The value to compare with .
/// The name of the parameter with which corresponds.
+ [Obsolete("Use ArgumentOutOfRangeException.ThrowIfNotEqual instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET8_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfNotEqual(
T value,
T other,
[CallerArgumentExpression(nameof(value))] string? paramName = null
)
- where T : IEquatable
- {
-#if NET8_0_OR_GREATER
- ArgumentOutOfRangeException.ThrowIfNotEqual(value, other, paramName);
-#else
- if (!EqualityComparer.Default.Equals(value, other))
- {
- ThrowArgumentOutOfRangeException(paramName, value, $"{paramName} ('{value}') must be equal to '{other}'.");
- }
-#endif
- }
+ where T : IEquatable => ArgumentOutOfRangeException.ThrowIfNotEqual(value, other, paramName);
}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfNull.cs b/src/NetEvolve.Arguments/Argument_ThrowIfNull.cs
index 10e7050..f1a5e63 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfNull.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfNull.cs
@@ -10,52 +10,26 @@ public static partial class Argument
/// Throws an if is null.
/// The reference type argument to validate as non-null.
/// The name of the parameter with which corresponds.
+ [Obsolete("Use ArgumentNullException.ThrowIfNull instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET6_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfNull(
[NotNull] object? argument,
[CallerArgumentExpression(nameof(argument))] string? paramName = null
- )
- {
-#if NET6_0_OR_GREATER
- ArgumentNullException.ThrowIfNull(argument, paramName);
-#else
- if (argument is null)
- {
- ThrowArgumentNullException(paramName);
- }
-#endif
- }
+ ) => ArgumentNullException.ThrowIfNull(argument, paramName);
/// Throws an if is null.
/// The reference type argument to validate as non-null.
/// The name of the parameter with which corresponds.
+ [Obsolete("Use ArgumentNullException.ThrowIfNull instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET7_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
#pragma warning disable S6640 // Make sure that using "unsafe" is safe here.
public static unsafe void ThrowIfNull(
#pragma warning restore S6640 // Make sure that using "unsafe" is safe here.
[NotNull] void* argument,
[CallerArgumentExpression(nameof(argument))] string? paramName = null
- )
- {
-#if NET7_0_OR_GREATER
- ArgumentNullException.ThrowIfNull(argument, paramName);
-#else
- if (argument == null)
- {
- ThrowArgumentNullException(paramName);
- }
-#endif
- }
+ ) => ArgumentNullException.ThrowIfNull(argument, paramName);
}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfNullOrEmpty.cs b/src/NetEvolve.Arguments/Argument_ThrowIfNullOrEmpty.cs
index 3462339..d1a58d5 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfNullOrEmpty.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfNullOrEmpty.cs
@@ -14,54 +14,26 @@ public static partial class Argument
/// The name of the parameter with which corresponds.
/// is null.
/// is empty.
+ [Obsolete("Use ArgumentException.ThrowIfNullOrEmpty instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET7_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfNullOrEmpty(
[NotNull] string? argument,
[CallerArgumentExpression(nameof(argument))] string? paramName = null
- )
- {
-#if NET7_0_OR_GREATER
- ArgumentException.ThrowIfNullOrEmpty(argument, paramName);
-#else
- if (argument is null)
- {
- ThrowArgumentNullException(paramName);
- }
-
- if (string.IsNullOrEmpty(argument))
- {
- ThrowArgumentException(paramName);
- }
-#endif
- }
+ ) => ArgumentException.ThrowIfNullOrEmpty(argument, paramName);
/// Throws an exception if is null or empty.
/// The string argument to validate as non-null and non-empty.
/// The name of the parameter with which corresponds.
/// is null.
/// is empty.
+ [Obsolete("Use ArgumentException.ThrowIfNullOrEmpty instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
[MethodImpl(MethodImplOptions.NoInlining)]
public static void ThrowIfNullOrEmpty(
[NotNull] IEnumerable argument,
[CallerArgumentExpression(nameof(argument))] string? paramName = null
- )
- {
- if (argument is null)
- {
- ThrowArgumentNullException(paramName);
- }
-
- if (argument.TryGetNonEnumeratedCount(out var count) && count == 0)
- {
- ThrowArgumentException(paramName);
- }
- }
+ ) => ArgumentException.ThrowIfNullOrEmpty(argument, paramName);
}
diff --git a/src/NetEvolve.Arguments/Argument_ThrowIfNullOrWhiteSpace.cs b/src/NetEvolve.Arguments/Argument_ThrowIfNullOrWhiteSpace.cs
index 7a44487..783ea1a 100644
--- a/src/NetEvolve.Arguments/Argument_ThrowIfNullOrWhiteSpace.cs
+++ b/src/NetEvolve.Arguments/Argument_ThrowIfNullOrWhiteSpace.cs
@@ -12,30 +12,12 @@ public static partial class Argument
/// The name of the parameter with which corresponds.
/// is null.
/// is empty or consists only of white-space characters.
+ [Obsolete("Use ArgumentException.ThrowIfNullOrWhiteSpace instead.")]
[DebuggerStepThrough]
[StackTraceHidden]
-#if NET8_0_OR_GREATER
[MethodImpl(MethodImplOptions.AggressiveInlining)]
-#else
- [MethodImpl(MethodImplOptions.NoInlining)]
-#endif
public static void ThrowIfNullOrWhiteSpace(
[NotNull] string? argument,
[CallerArgumentExpression(nameof(argument))] string? paramName = null
- )
- {
-#if NET8_0_OR_GREATER
- ArgumentException.ThrowIfNullOrWhiteSpace(argument, paramName);
-#else
- if (argument is null)
- {
- ThrowArgumentNullException(paramName);
- }
-
- if (string.IsNullOrWhiteSpace(argument))
- {
- ThrowArgumentException(paramName);
- }
-#endif
- }
+ ) => ArgumentException.ThrowIfNullOrWhiteSpace(argument, paramName);
}
diff --git a/src/NetEvolve.Arguments/NetEvolve.Arguments.csproj b/src/NetEvolve.Arguments/NetEvolve.Arguments.csproj
index 817ca5a..cb04da8 100644
--- a/src/NetEvolve.Arguments/NetEvolve.Arguments.csproj
+++ b/src/NetEvolve.Arguments/NetEvolve.Arguments.csproj
@@ -1,21 +1,12 @@
- netstandard2.0;net8.0;net9.0;net10.0
- A library that provides compatible `ThrowIf` methods for .NET / C# for older runtimes.
+ $(_ProjectTargetFrameworks)
+ A universal polyfill library that provides modern ArgumentNullException.ThrowIf* and ArgumentException.ThrowIf* helper methods across all .NET runtimes (.NET Standard 2.0+, .NET Framework 4.6.2+, .NET 6.0+), enabling consistent argument validation patterns regardless of target framework version.
true
$(MSBuildProjectName)
https://github.com/dailydevops/arguments
https://github.com/dailydevops/arguments.git
- guard;clausel;exceptions
+ guard;clause;exceptions;argument-validation;polyfill;null-check;parameter-validation;defensive-programming;argument-exception;argumentnullexception
$(PackageProjectUrl)/releases/
- 2023
-
- true
-
-
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
diff --git a/src/NetEvolve.Arguments/Polyfills/CallerArgumentExpressionAttribute.cs b/src/NetEvolve.Arguments/Polyfills/CallerArgumentExpressionAttribute.cs
new file mode 100644
index 0000000..57c1e6e
--- /dev/null
+++ b/src/NetEvolve.Arguments/Polyfills/CallerArgumentExpressionAttribute.cs
@@ -0,0 +1,34 @@
+#if !NETCOREAPP3_0_OR_GREATER
+
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System.Runtime.CompilerServices;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+using Diagnostics;
+using Diagnostics.CodeAnalysis;
+
+///
+/// Indicates that a parameter captures the expression passed for another parameter as a string.
+///
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[AttributeUsage(AttributeTargets.Parameter)]
+internal sealed class CallerArgumentExpressionAttribute : Attribute
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public CallerArgumentExpressionAttribute(string parameterName) => ParameterName = parameterName;
+
+ ///
+ /// Gets the name of the parameter whose expression should be captured as a string.
+ ///
+ public string ParameterName { get; }
+}
+
+#else
+using System.Runtime.CompilerServices;
+
+[assembly: TypeForwardedTo(typeof(CallerArgumentExpressionAttribute))]
+#endif
diff --git a/src/NetEvolve.Arguments/Polyfills/DoesNotReturnAttribute.cs b/src/NetEvolve.Arguments/Polyfills/DoesNotReturnAttribute.cs
new file mode 100644
index 0000000..6faf68a
--- /dev/null
+++ b/src/NetEvolve.Arguments/Polyfills/DoesNotReturnAttribute.cs
@@ -0,0 +1,19 @@
+#if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
+
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System.Diagnostics.CodeAnalysis;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+///
+/// Specifies that a method that will never return under any circumstance.
+///
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[AttributeUsage(validOn: AttributeTargets.Method, Inherited = false)]
+internal sealed class DoesNotReturnAttribute : Attribute;
+#else
+using System.Runtime.CompilerServices;
+
+[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute))]
+#endif
diff --git a/src/NetEvolve.Arguments/Polyfills/DoesNotReturnIfAttribute.cs b/src/NetEvolve.Arguments/Polyfills/DoesNotReturnIfAttribute.cs
new file mode 100644
index 0000000..453cdae
--- /dev/null
+++ b/src/NetEvolve.Arguments/Polyfills/DoesNotReturnIfAttribute.cs
@@ -0,0 +1,34 @@
+#if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
+
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System.Diagnostics.CodeAnalysis;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+///
+/// Specifies that the method will not return if the associated
+/// parameter is passed the specified value.
+///
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[AttributeUsage(AttributeTargets.Parameter)]
+internal sealed class DoesNotReturnIfAttribute : Attribute
+{
+ ///
+ /// Gets the condition parameter value.
+ /// Code after the method is considered unreachable by diagnostics if the argument
+ /// to the associated parameter matches this value.
+ ///
+ public bool ParameterValue { get; }
+
+ ///
+ /// Initializes a new instance of the
+ /// class with the specified parameter value.
+ ///
+ public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
+}
+#else
+using System.Runtime.CompilerServices;
+
+[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.DoesNotReturnIfAttribute))]
+#endif
diff --git a/src/NetEvolve.Arguments/Polyfills/IEnumerable_TryGetNonEnumeratedCount.cs b/src/NetEvolve.Arguments/Polyfills/IEnumerable_TryGetNonEnumeratedCount.cs
new file mode 100644
index 0000000..847f5e6
--- /dev/null
+++ b/src/NetEvolve.Arguments/Polyfills/IEnumerable_TryGetNonEnumeratedCount.cs
@@ -0,0 +1,56 @@
+#if !NET6_0_OR_GREATER
+
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System.Linq;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+internal static class IEnumerableExtensions
+{
+ /// Attempts to determine the number of elements in a sequence without forcing an enumeration.
+ /// The type of elements in the source sequence.
+ /// The source sequence to get the count from.
+ /// The count of elements in the sequence, if available; otherwise, 0.
+ ///
+ /// if the number of elements could be determined without enumeration;
+ /// otherwise, .
+ ///
+ ///
+ ///
+ /// This method attempts to determine the number of elements in a sequence without forcing
+ /// an enumeration by checking for common collection implementations such as
+ /// and .
+ ///
+ ///
+ /// If the sequence does not implement one of these interfaces or the count is not immediately
+ /// available, the method returns and sets
+ /// to 0.
+ ///
+ ///
+ ///
+ public static bool TryGetNonEnumeratedCount(this IEnumerable target, out int count)
+ {
+ if (target is ICollection genericCollection)
+ {
+ count = genericCollection.Count;
+ return true;
+ }
+
+ if (target is ICollection collection)
+ {
+ count = collection.Count;
+ return true;
+ }
+
+ count = 0;
+ return false;
+ }
+}
+#endif
diff --git a/src/NetEvolve.Arguments/Polyfills/ModuleInitializerAttribute.cs b/src/NetEvolve.Arguments/Polyfills/ModuleInitializerAttribute.cs
new file mode 100644
index 0000000..e15e4ef
--- /dev/null
+++ b/src/NetEvolve.Arguments/Polyfills/ModuleInitializerAttribute.cs
@@ -0,0 +1,25 @@
+#if !NET5_0_OR_GREATER
+
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System.Runtime.CompilerServices;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using static AttributeTargets;
+
+///
+/// Used to indicate to the compiler that a method should be called
+/// in its containing module's initializer.
+///
+///
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[AttributeUsage(validOn: Method, Inherited = false)]
+internal sealed class ModuleInitializerAttribute : Attribute;
+#else
+using System.Runtime.CompilerServices;
+
+[assembly: TypeForwardedTo(typeof(ModuleInitializerAttribute))]
+#endif
diff --git a/src/NetEvolve.Arguments/Polyfills/NotNullAttribute.cs b/src/NetEvolve.Arguments/Polyfills/NotNullAttribute.cs
new file mode 100644
index 0000000..43ea81f
--- /dev/null
+++ b/src/NetEvolve.Arguments/Polyfills/NotNullAttribute.cs
@@ -0,0 +1,22 @@
+#if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
+
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System.Diagnostics.CodeAnalysis;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+using static AttributeTargets;
+
+///
+/// Specifies that an output is not even if the
+/// corresponding type allows it.
+///
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[AttributeUsage(validOn: Field | Parameter | Property | ReturnValue)]
+internal sealed class NotNullAttribute : Attribute;
+#else
+using System.Runtime.CompilerServices;
+
+[assembly: TypeForwardedTo(typeof(System.Diagnostics.CodeAnalysis.NotNullAttribute))]
+#endif
diff --git a/src/NetEvolve.Arguments/Polyfills/StackTraceHiddenAttribute.cs b/src/NetEvolve.Arguments/Polyfills/StackTraceHiddenAttribute.cs
new file mode 100644
index 0000000..9ffdcbc
--- /dev/null
+++ b/src/NetEvolve.Arguments/Polyfills/StackTraceHiddenAttribute.cs
@@ -0,0 +1,22 @@
+#if !NET6_0_OR_GREATER
+#pragma warning disable IDE0130 // Namespace does not match folder structure
+namespace System.Diagnostics;
+
+#pragma warning restore IDE0130 // Namespace does not match folder structure
+
+using System.Diagnostics.CodeAnalysis;
+using static AttributeTargets;
+
+///
+/// Types and Methods attributed with StackTraceHidden will be omitted from the stack trace text shown in StackTrace.ToString()
+/// and Exception.StackTrace
+///
+[ExcludeFromCodeCoverage]
+[DebuggerNonUserCode]
+[AttributeUsage(validOn: Class | Method | Constructor | Struct, Inherited = false)]
+internal sealed class StackTraceHiddenAttribute : Attribute;
+#else
+using System.Runtime.CompilerServices;
+
+[assembly: TypeForwardedTo(typeof(System.Diagnostics.StackTraceHiddenAttribute))]
+#endif
diff --git a/tests/NetEvolve.Arguments.Tests.Unit/ArgumentExceptionPolyfillsTests.cs b/tests/NetEvolve.Arguments.Tests.Unit/ArgumentExceptionPolyfillsTests.cs
new file mode 100644
index 0000000..e47c858
--- /dev/null
+++ b/tests/NetEvolve.Arguments.Tests.Unit/ArgumentExceptionPolyfillsTests.cs
@@ -0,0 +1,155 @@
+namespace NetEvolve.Arguments.Tests.Unit;
+
+using System;
+using System.Collections.Generic;
+
+public sealed class ArgumentExceptionPolyfillsTests
+{
+ [Test]
+ public void ThrowIfNullOrEmpty_String_WhenArgumentIsNull_ThrowsArgumentNullException()
+ {
+ // Arrange
+ string? argument = null;
+
+ // Act
+ void Act() => ArgumentException.ThrowIfNullOrEmpty(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public void ThrowIfNullOrEmpty_String_WhenArgumentIsEmpty_ThrowsArgumentException()
+ {
+ // Arrange
+ var argument = string.Empty;
+
+ // Act
+ void Act() => ArgumentException.ThrowIfNullOrEmpty(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public async Task ThrowIfNullOrEmpty_String_WhenArgumentIsValid_DoesNotThrow()
+ {
+ // Arrange
+ var argument = "valid";
+
+ // Act & Assert
+ ArgumentException.ThrowIfNullOrEmpty(argument);
+ _ = await Assert.That(argument).IsNotNullOrWhiteSpace();
+ }
+
+ [Test]
+ public void ThrowIfNullOrWhiteSpace_WhenArgumentIsNull_ThrowsArgumentNullException()
+ {
+ // Arrange
+ string? argument = null;
+
+ // Act
+ void Act() => ArgumentException.ThrowIfNullOrWhiteSpace(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public void ThrowIfNullOrWhiteSpace_WhenArgumentIsEmpty_ThrowsArgumentException()
+ {
+ // Arrange
+ var argument = string.Empty;
+
+ // Act
+ void Act() => ArgumentException.ThrowIfNullOrWhiteSpace(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public void ThrowIfNullOrWhiteSpace_WhenArgumentIsWhiteSpace_ThrowsArgumentException()
+ {
+ // Arrange
+ var argument = " ";
+
+ // Act
+ void Act() => ArgumentException.ThrowIfNullOrWhiteSpace(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ [Arguments("valid")]
+ [Arguments("test")]
+ [Arguments("a")]
+ public async Task ThrowIfNullOrWhiteSpace_WhenArgumentIsValid_DoesNotThrow(string argument)
+ {
+ // Act & Assert
+ ArgumentException.ThrowIfNullOrWhiteSpace(argument);
+ _ = await Assert.That(argument).IsNotNullOrWhiteSpace();
+ }
+
+ [Test]
+ public void ThrowIfNullOrEmpty_Enumerable_WhenArgumentIsNull_ThrowsArgumentNullException()
+ {
+ // Arrange
+ IEnumerable? argument = null;
+
+ // Act
+ void Act() => ArgumentException.ThrowIfNullOrEmpty(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public void ThrowIfNullOrEmpty_Enumerable_WhenArgumentIsEmptyArray_ThrowsArgumentException()
+ {
+ // Arrange
+ var argument = Array.Empty();
+
+ // Act
+ void Act() => ArgumentException.ThrowIfNullOrEmpty(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public void ThrowIfNullOrEmpty_Enumerable_WhenArgumentIsEmptyList_ThrowsArgumentException()
+ {
+ // Arrange
+ var argument = new List();
+
+ // Act
+ void Act() => ArgumentException.ThrowIfNullOrEmpty(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public async Task ThrowIfNullOrEmpty_Enumerable_WhenArgumentHasElements_DoesNotThrow()
+ {
+ // Arrange
+ var argument = new[] { 1, 2, 3 };
+
+ // Act & Assert
+ ArgumentException.ThrowIfNullOrEmpty(argument);
+ _ = await Assert.That(argument).HasCount().EqualTo(3);
+ }
+
+ [Test]
+ public async Task ThrowIfNullOrEmpty_Enumerable_WhenArgumentIsSingleElement_DoesNotThrow()
+ {
+ // Arrange
+ var argument = new List { "item" };
+
+ // Act & Assert
+ ArgumentException.ThrowIfNullOrEmpty(argument);
+ _ = await Assert.That(argument).HasCount().EqualTo(1);
+ }
+}
diff --git a/tests/NetEvolve.Arguments.Tests.Unit/ArgumentNullExceptionPolyfillsTests.cs b/tests/NetEvolve.Arguments.Tests.Unit/ArgumentNullExceptionPolyfillsTests.cs
new file mode 100644
index 0000000..6ec56ba
--- /dev/null
+++ b/tests/NetEvolve.Arguments.Tests.Unit/ArgumentNullExceptionPolyfillsTests.cs
@@ -0,0 +1,97 @@
+namespace NetEvolve.Arguments.Tests.Unit;
+
+using System;
+
+public sealed class ArgumentNullExceptionPolyfillsTests
+{
+ [Test]
+ public void ThrowIfNull_Object_WhenArgumentIsNull_ThrowsArgumentNullException()
+ {
+ // Arrange
+ object? argument = null;
+
+ // Act
+ void Act() => ArgumentNullException.ThrowIfNull(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public async Task ThrowIfNull_Object_WhenArgumentIsNotNull_DoesNotThrow()
+ {
+ // Arrange
+ var argument = new object();
+
+ // Act & Assert
+ ArgumentNullException.ThrowIfNull(argument);
+ }
+
+ [Test]
+ public void ThrowIfNull_String_WhenArgumentIsNull_ThrowsArgumentNullException()
+ {
+ // Arrange
+ string? argument = null;
+
+ // Act
+ void Act() => ArgumentNullException.ThrowIfNull(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public async Task ThrowIfNull_String_WhenArgumentIsNotNull_DoesNotThrow()
+ {
+ // Arrange
+ var argument = "test";
+
+ // Act & Assert
+ ArgumentNullException.ThrowIfNull(argument);
+ _ = await Assert.That(argument).IsNotNull();
+ }
+
+ [Test]
+ public unsafe void ThrowIfNull_Pointer_WhenArgumentIsNull_ThrowsArgumentNullException()
+ {
+ // Arrange
+ int* argument = null;
+
+ // Act
+ void Act() => ArgumentNullException.ThrowIfNull(argument);
+
+ // Assert
+ _ = Assert.Throws("argument", Act);
+ }
+
+ [Test]
+ public unsafe void ThrowIfNull_Pointer_WhenArgumentIsNotNull_DoesNotThrow()
+ {
+ // Arrange
+ var value = 42;
+ var argument = &value;
+
+ // Act & Assert
+ ArgumentNullException.ThrowIfNull(argument);
+ }
+
+ [Test]
+ public async Task ThrowIfNull_ReferenceType_WhenArgumentIsNotNull_DoesNotThrow()
+ {
+ // Arrange
+ var argument = new object();
+
+ // Act & Assert
+ ArgumentNullException.ThrowIfNull(argument);
+ }
+
+ [Test]
+ public async Task ThrowIfNull_ValueType_WhenArgumentHasValue_DoesNotThrow()
+ {
+ // Arrange
+ var argument = (object)123;
+
+ // Act & Assert
+ ArgumentNullException.ThrowIfNull(argument);
+ }
+}
diff --git a/tests/NetEvolve.Arguments.Tests.Unit/ArgumentOutOfRangeExceptionPolyfillsTests.cs b/tests/NetEvolve.Arguments.Tests.Unit/ArgumentOutOfRangeExceptionPolyfillsTests.cs
new file mode 100644
index 0000000..5e2664d
--- /dev/null
+++ b/tests/NetEvolve.Arguments.Tests.Unit/ArgumentOutOfRangeExceptionPolyfillsTests.cs
@@ -0,0 +1,417 @@
+namespace NetEvolve.Arguments.Tests.Unit;
+
+using System;
+
+public sealed class ArgumentOutOfRangeExceptionPolyfillsTests
+{
+ [Test]
+ public void ThrowIfZero_Int_WhenValueIsZero_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = 0;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfZero(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ [Arguments(1)]
+ [Arguments(-1)]
+ [Arguments(100)]
+ [Arguments(-100)]
+ public async Task ThrowIfZero_Int_WhenValueIsNotZero_DoesNotThrow(int value)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfZero(value);
+ _ = await Assert.That(value).IsNotEqualTo(0);
+ }
+
+ [Test]
+ public void ThrowIfZero_Double_WhenValueIsZero_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = 0.0;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfZero(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ [Arguments(1.5)]
+ [Arguments(-1.5)]
+ public async Task ThrowIfZero_Double_WhenValueIsNotZero_DoesNotThrow(double value)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfZero(value);
+ _ = await Assert.That(value).IsNotEqualTo(0.0);
+ }
+
+ [Test]
+ public void ThrowIfNegative_Int_WhenValueIsNegative_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = -1;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNegative(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ [Arguments(0)]
+ [Arguments(1)]
+ [Arguments(100)]
+ public async Task ThrowIfNegative_Int_WhenValueIsNonNegative_DoesNotThrow(int value)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfNegative(value);
+ _ = await Assert.That(value).IsGreaterThanOrEqualTo(0);
+ }
+
+ [Test]
+ public void ThrowIfNegative_Double_WhenValueIsNegative_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = -1.5;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNegative(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ [Arguments(0.0)]
+ [Arguments(1.5)]
+ public async Task ThrowIfNegative_Double_WhenValueIsNonNegative_DoesNotThrow(double value)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfNegative(value);
+ _ = await Assert.That(value).IsGreaterThanOrEqualTo(0.0);
+ }
+
+ [Test]
+ public void ThrowIfNegative_NInt_WhenValueIsNegative_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ nint value = -1;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNegative(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ public async Task ThrowIfNegative_NInt_WhenValueIsNonNegative_DoesNotThrow()
+ {
+ // Arrange
+ nint value = 0;
+
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfNegative(value);
+ }
+
+ [Test]
+ [Arguments(0)]
+ [Arguments(-1)]
+ [Arguments(-100)]
+ public void ThrowIfNegativeOrZero_Int_WhenValueIsNegativeOrZero_ThrowsArgumentOutOfRangeException(int value)
+ {
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
+
+ // Assert
+ _ = Assert.Throws(nameof(value), Act);
+ }
+
+ [Test]
+ [Arguments(1)]
+ [Arguments(100)]
+ public async Task ThrowIfNegativeOrZero_Int_WhenValueIsPositive_DoesNotThrow(int value)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
+ _ = await Assert.That(value).IsGreaterThan(0);
+ }
+
+ [Test]
+ public void ThrowIfNegativeOrZero_Double_WhenValueIsZero_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = 0.0;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ public void ThrowIfNegativeOrZero_Double_WhenValueIsNegative_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = -1.5;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ [Arguments(0.1)]
+ [Arguments(1.5)]
+ public async Task ThrowIfNegativeOrZero_Double_WhenValueIsPositive_DoesNotThrow(double value)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
+ _ = await Assert.That(value).IsGreaterThan(0.0);
+ }
+
+ [Test]
+ public void ThrowIfNegativeOrZero_NInt_WhenValueIsZero_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ nint value = 0;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ public void ThrowIfNegativeOrZero_NInt_WhenValueIsNegative_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ nint value = -1;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ public async Task ThrowIfNegativeOrZero_NInt_WhenValueIsPositive_DoesNotThrow()
+ {
+ // Arrange
+ nint value = 1;
+
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfNegativeOrZero(value);
+ }
+
+ [Test]
+ public void ThrowIfEqual_Int_WhenValuesAreEqual_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = 5;
+ var other = 5;
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfEqual(value, other);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ [Arguments(1, 2)]
+ [Arguments(10, 5)]
+ [Arguments(-1, 1)]
+ public async Task ThrowIfEqual_Int_WhenValuesAreNotEqual_DoesNotThrow(int value, int other)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfEqual(value, other);
+ _ = await Assert.That(value).IsNotEqualTo(other);
+ }
+
+ [Test]
+ public void ThrowIfEqual_String_WhenValuesAreEqual_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = "test";
+ var other = "test";
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfEqual(value, other);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ public async Task ThrowIfEqual_String_WhenValuesAreNotEqual_DoesNotThrow()
+ {
+ // Arrange
+ var value = "test1";
+ var other = "test2";
+
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfEqual(value, other);
+ _ = await Assert.That(value).IsNotEqualTo(other);
+ }
+
+ [Test]
+ [Arguments(1, 2)]
+ [Arguments(10, 5)]
+ [Arguments(-1, 1)]
+ public void ThrowIfNotEqual_Int_WhenValuesAreNotEqual_ThrowsArgumentOutOfRangeException(int value, int other)
+ {
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNotEqual(value, other);
+
+ // Assert
+ _ = Assert.Throws(nameof(value), Act);
+ }
+
+ [Test]
+ public async Task ThrowIfNotEqual_Int_WhenValuesAreEqual_DoesNotThrow()
+ {
+ // Arrange
+ var value = 5;
+ var other = 5;
+
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfNotEqual(value, other);
+ _ = await Assert.That(value).IsEqualTo(other);
+ }
+
+ [Test]
+ public void ThrowIfNotEqual_String_WhenValuesAreNotEqual_ThrowsArgumentOutOfRangeException()
+ {
+ // Arrange
+ var value = "test1";
+ var other = "test2";
+
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfNotEqual(value, other);
+
+ // Assert
+ _ = Assert.Throws("value", Act);
+ }
+
+ [Test]
+ public async Task ThrowIfNotEqual_String_WhenValuesAreEqual_DoesNotThrow()
+ {
+ // Arrange
+ var value = "test";
+ var other = "test";
+
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfNotEqual(value, other);
+ _ = await Assert.That(value).IsEqualTo(other);
+ }
+
+ [Test]
+ [Arguments(6, 5)]
+ [Arguments(100, 50)]
+ public void ThrowIfGreaterThan_Int_WhenValueIsGreater_ThrowsArgumentOutOfRangeException(int value, int other)
+ {
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfGreaterThan(value, other);
+
+ // Assert
+ _ = Assert.Throws(nameof(value), Act);
+ }
+
+ [Test]
+ [Arguments(5, 5)]
+ [Arguments(4, 5)]
+ [Arguments(1, 100)]
+ public async Task ThrowIfGreaterThan_Int_WhenValueIsNotGreater_DoesNotThrow(int value, int other)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfGreaterThan(value, other);
+ _ = await Assert.That(value).IsLessThanOrEqualTo(other);
+ }
+
+ [Test]
+ [Arguments(6, 5)]
+ [Arguments(5, 5)]
+ public void ThrowIfGreaterThanOrEqual_Int_WhenValueIsGreaterOrEqual_ThrowsArgumentOutOfRangeException(
+ int value,
+ int other
+ )
+ {
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(value, other);
+
+ // Assert
+ _ = Assert.Throws(nameof(value), Act);
+ }
+
+ [Test]
+ [Arguments(4, 5)]
+ [Arguments(1, 100)]
+ public async Task ThrowIfGreaterThanOrEqual_Int_WhenValueIsLess_DoesNotThrow(int value, int other)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(value, other);
+ _ = await Assert.That(value).IsLessThan(other);
+ }
+
+ [Test]
+ [Arguments(4, 5)]
+ [Arguments(1, 100)]
+ public void ThrowIfLessThan_Int_WhenValueIsLess_ThrowsArgumentOutOfRangeException(int value, int other)
+ {
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfLessThan(value, other);
+
+ // Assert
+ _ = Assert.Throws(nameof(value), Act);
+ }
+
+ [Test]
+ [Arguments(5, 5)]
+ [Arguments(6, 5)]
+ [Arguments(100, 1)]
+ public async Task ThrowIfLessThan_Int_WhenValueIsNotLess_DoesNotThrow(int value, int other)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfLessThan(value, other);
+ _ = await Assert.That(value).IsGreaterThanOrEqualTo(other);
+ }
+
+ [Test]
+ [Arguments(4, 5)]
+ [Arguments(5, 5)]
+ public void ThrowIfLessThanOrEqual_Int_WhenValueIsLessOrEqual_ThrowsArgumentOutOfRangeException(
+ int value,
+ int other
+ )
+ {
+ // Act
+ void Act() => ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, other);
+
+ // Assert
+ _ = Assert.Throws(nameof(value), Act);
+ }
+
+ [Test]
+ [Arguments(6, 5)]
+ [Arguments(100, 1)]
+ public async Task ThrowIfLessThanOrEqual_Int_WhenValueIsGreater_DoesNotThrow(int value, int other)
+ {
+ // Act & Assert
+ ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(value, other);
+ _ = await Assert.That(value).IsGreaterThan(other);
+ }
+}
diff --git a/tests/NetEvolve.Arguments.Tests.Unit/ArgumentTests.cs b/tests/NetEvolve.Arguments.Tests.Unit/ArgumentTests.cs
deleted file mode 100644
index 74ec165..0000000
--- a/tests/NetEvolve.Arguments.Tests.Unit/ArgumentTests.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace NetEvolve.Arguments.Tests.Unit;
-
-using System.Diagnostics.CodeAnalysis;
-using NetEvolve.Extensions.TUnit;
-
-[ExcludeFromCodeCoverage]
-[UnitTest]
-public sealed partial class ArgumentTests { }
diff --git a/tests/NetEvolve.Arguments.Tests.Unit/NetEvolve.Arguments.Tests.Unit.csproj b/tests/NetEvolve.Arguments.Tests.Unit/NetEvolve.Arguments.Tests.Unit.csproj
index 3167f14..4d3f840 100644
--- a/tests/NetEvolve.Arguments.Tests.Unit/NetEvolve.Arguments.Tests.Unit.csproj
+++ b/tests/NetEvolve.Arguments.Tests.Unit/NetEvolve.Arguments.Tests.Unit.csproj
@@ -1,9 +1,10 @@
- net8.0;net9.0;net10.0
+ $(_TestTargetFrameworks)
true
- $(NoWarn);NU1701
+ $(NoWarn);NU1701;CS0618
true
+ Exe
@@ -14,4 +15,15 @@
+
+
+
+
+ x64
+ win-x64
+
+
+ x86
+ win-x86
+