Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
dotnet-version: 8.0.x
- name: Restore dependencies
run: dotnet restore ./Code/Light.GuardClauses.AllProjects.sln
- name: Build Analyzer
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-on-nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
dotnetVersion:
description: "The version of .NET to use"
required: false
default: "7.0.x"
default: "8.0.x"

jobs:
release-to-nuget:
Expand All @@ -20,7 +20,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
dotnet-version: ${{ github.event.inputs.dotnetVersion || '7.0.x' }}
dotnet-version: ${{ github.event.inputs.dotnetVersion || '8.0.x' }}
- name: Prepare SNK file
env:
LIGHT_GUARDCLAUSES_SNK: ${{ secrets.LIGHT_GUARDCLAUSES_SNK }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public ConsoleColor NoFlagsBaseVersion()

public static class MustBeValidEnumValueExtensionMethods
{
public static T OldMustBeValidEnumValue<T>(this T parameter, string parameterName = null, string message = null, Func<Exception> exception = null) where T : Enum
public static T OldMustBeValidEnumValue<T>(this T parameter, string parameterName = null, string message = null, Func<Exception> exception = null) where T : struct, Enum
{
if (parameter.IsValidEnumValue())
return parameter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0;net48</TargetFrameworks>
<TargetFrameworks>net8.0;net48</TargetFrameworks>
<LangVersion>11</LangVersion>
</PropertyGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static class IsValidEnumValueTests
[InlineData(UInt64Enum.AllLow | UInt64Enum.High1)]
[InlineData(UInt64Enum.AllHigh)]
[InlineData(UInt64Enum.MaxValue)]
public static void EnumValueValid<T>(T enumValue) where T : Enum, IComparable
public static void EnumValueValid<T>(T enumValue) where T : struct, Enum, IComparable
{
if (enumValue is BindingFlags)
{
Expand All @@ -42,6 +42,7 @@ public static void EnumValueValid<T>(T enumValue) where T : Enum, IComparable
[InlineData(int.MinValue)]
[InlineData(2048)]
[InlineData(2055)]
[InlineData(4096)]
[InlineData(int.MaxValue)]
public static void InvalidNumberStyles(int invalidValue) => ((NumberStyles) invalidValue).IsValidEnumValue().Should().BeFalse();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,28 @@ public static void CallerArgumentExpression()
act.Should().Throw<EmptyGuidException>()
.WithParameterName(nameof(emptyGuid));
}
}

public class Entity(Guid id)
{
public Guid Id { get; } = id.MustNotBeEmpty();
}

[Fact]
public static void PrimaryConstructorValidArgument()
{
var guid = Guid.NewGuid();
var entity = new Entity(guid);

entity.Id.Should().Be(guid);
}

[Fact]
public static void PrimaryConstructorInvalidArgument()
{
var act = () => new Entity(Guid.Empty);

act.Should().Throw<EmptyGuidException>()
.And.Message.Should().StartWith("id must be a valid GUID, but it actually is an empty one.");
}
}

12 changes: 6 additions & 6 deletions Code/Light.GuardClauses.Tests/Light.GuardClauses.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
<Import Project="TargetFrameworks.props" Condition="Exists('TargetFrameworks.props')" />

<PropertyGroup>
<TargetFrameworks Condition="'$(TargetFrameworks)' == ''">net7.0</TargetFrameworks>
<TargetFrameworks Condition="'$(TargetFrameworks)' == ''">net8.0</TargetFrameworks>
<IsPackable>false</IsPackable>
<LangVersion>11.0</LangVersion>
<LangVersion>12</LangVersion>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
<PackageReference Include="FluentAssertions" Version="6.11.0" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="xunit" Version="2.5.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.1" />
<ProjectReference Include="..\Light.GuardClauses\Light.GuardClauses.csproj" />
</ItemGroup>

Expand Down
6 changes: 4 additions & 2 deletions Code/Light.GuardClauses/CallerArgumentExpressionAttribute.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// ReSharper disable once CheckNamespace -- CallerArgumentExpression must be in exactly this namespace
#if !NET8_0
// ReSharper disable once CheckNamespace -- CallerArgumentExpression must be in exactly this namespace
namespace System.Runtime.CompilerServices;

[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
Expand All @@ -10,4 +11,5 @@ public CallerArgumentExpressionAttribute(string parameterName)
}

public string ParameterName { get; }
}
}
#endif
6 changes: 3 additions & 3 deletions Code/Light.GuardClauses/Check.CommonAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ public static T MustBeOfType<T>([ValidatedNotNull, NoEnumeration] this object? p
/// <typeparam name="T">The type of the enum.</typeparam>
/// <param name="parameter">The enum value to be checked.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool IsValidEnumValue<T>(this T parameter) where T : Enum
public static bool IsValidEnumValue<T>(this T parameter) where T : struct, Enum
=> EnumInfo<T>.IsValidEnumValue(parameter);

/// <summary>
Expand All @@ -189,7 +189,7 @@ public static bool IsValidEnumValue<T>(this T parameter) where T : Enum
/// <param name="message">The message that will be passed to the resulting exception (optional).</param>
/// <exception cref="EnumValueNotDefinedException">Thrown when <paramref name="parameter" /> is no valid enum value.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T MustBeValidEnumValue<T>(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : Enum
public static T MustBeValidEnumValue<T>(this T parameter, [CallerArgumentExpression("parameter")] string? parameterName = null, string? message = null) where T : struct, Enum
{
if (!EnumInfo<T>.IsValidEnumValue(parameter))
Throw.EnumValueNotDefined(parameter, parameterName, message);
Expand All @@ -207,7 +207,7 @@ public static T MustBeValidEnumValue<T>(this T parameter, [CallerArgumentExpress
/// <exception cref="Exception">Your custom exception thrown when <paramref name="parameter" /> is no valid enum value, or when <typeparamref name="T" /> is no enum type.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ContractAnnotation("exceptionFactory:null => halt")]
public static T MustBeValidEnumValue<T>(this T parameter, Func<T, Exception> exceptionFactory) where T : Enum
public static T MustBeValidEnumValue<T>(this T parameter, Func<T, Exception> exceptionFactory) where T : struct, Enum
{
if (!EnumInfo<T>.IsValidEnumValue(parameter))
Throw.CustomException(exceptionFactory, parameter);
Expand Down
91 changes: 72 additions & 19 deletions Code/Light.GuardClauses/Check.TypeAssertions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using JetBrains.Annotations;
#if NET8_0
using System.Diagnostics.CodeAnalysis;
#endif

namespace Light.GuardClauses;

Expand Down Expand Up @@ -41,11 +44,19 @@ private static bool CheckTypeEquivalency(Type type, Type other)
/// <param name="interfaceType">The interface type that <paramref name="type" /> should implement.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type" /> or <paramref name="interfaceType" /> is null.</exception>
[ContractAnnotation("type:null => halt; interfaceType:null => halt")]
public static bool Implements([ValidatedNotNull] this Type type, [ValidatedNotNull] Type interfaceType)
public static bool Implements(
#if NET8_0
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
[ValidatedNotNull]
this Type type,
[ValidatedNotNull] Type interfaceType
)
{
interfaceType.MustNotBeNull(nameof(interfaceType));
var implementedInterfaces = type.MustNotBeNull(nameof(type)).GetInterfaces();
type.MustNotBeNull();
interfaceType.MustNotBeNull();

var implementedInterfaces = type.GetInterfaces();
for (var i = 0; i < implementedInterfaces.Length; ++i)
{
if (interfaceType.IsEquivalentTypeTo(implementedInterfaces[i]))
Expand All @@ -64,12 +75,21 @@ public static bool Implements([ValidatedNotNull] this Type type, [ValidatedNotNu
/// <param name="typeComparer">The equality comparer used to compare the interface types.</param>
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type" />, or <paramref name="interfaceType" />, or <paramref name="typeComparer" /> is null.</exception>
[ContractAnnotation("type:null => halt; interfaceType:null => halt; typeComparer:null => halt")]
public static bool Implements([ValidatedNotNull] this Type type, [ValidatedNotNull] Type interfaceType, [ValidatedNotNull] IEqualityComparer<Type> typeComparer)
public static bool Implements(
#if NET8_0
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
[ValidatedNotNull]
this Type type,
[ValidatedNotNull] Type interfaceType,
[ValidatedNotNull] IEqualityComparer<Type> typeComparer
)
{
interfaceType.MustNotBeNull(nameof(interfaceType));
typeComparer.MustNotBeNull(nameof(typeComparer));
type.MustNotBeNull();
interfaceType.MustNotBeNull();
typeComparer.MustNotBeNull();

var implementedInterfaces = type.MustNotBeNull(nameof(type)).GetInterfaces();
var implementedInterfaces = type.GetInterfaces();
for (var i = 0; i < implementedInterfaces.Length; ++i)
{
if (typeComparer.Equals(implementedInterfaces[i], interfaceType))
Expand All @@ -88,7 +108,12 @@ public static bool Implements([ValidatedNotNull] this Type type, [ValidatedNotNu
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type" /> or <paramref name="otherType" /> is null.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ContractAnnotation("type:null => halt; otherType:null => halt")]
public static bool IsOrImplements([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType) =>
public static bool IsOrImplements(
#if NET8_0
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
[ValidatedNotNull] this Type type,
[ValidatedNotNull] Type otherType) =>
type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType);

/// <summary>
Expand All @@ -102,7 +127,13 @@ public static bool IsOrImplements([ValidatedNotNull] this Type type, [ValidatedN
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type" /> or <paramref name="otherType" /> is null.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ContractAnnotation("type:null => halt; otherType:null => halt")]
public static bool IsOrImplements([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType, [ValidatedNotNull] IEqualityComparer<Type> typeComparer) =>
public static bool IsOrImplements(
#if NET8_0
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
[ValidatedNotNull] this Type type,
[ValidatedNotNull] Type otherType,
[ValidatedNotNull] IEqualityComparer<Type> typeComparer) =>
typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type.MustNotBeNull(nameof(type)), otherType.MustNotBeNull(nameof(otherType))) || type.Implements(otherType, typeComparer);

/// <summary>
Expand Down Expand Up @@ -190,11 +221,16 @@ public static bool IsOrDerivesFrom([ValidatedNotNull] this Type type, [Validated
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type" /> or <paramref name="baseClassOrInterfaceType" /> is null.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt")]
public static bool InheritsFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type baseClassOrInterfaceType) =>
public static bool InheritsFrom(
#if NET8_0
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
[ValidatedNotNull] this Type type,
[ValidatedNotNull] Type baseClassOrInterfaceType) =>
baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType))
.IsInterface
? type.Implements(baseClassOrInterfaceType)
: type.DerivesFrom(baseClassOrInterfaceType);
.IsInterface ?
type.Implements(baseClassOrInterfaceType) :
type.DerivesFrom(baseClassOrInterfaceType);

/// <summary>
/// Checks if the given type derives from the specified base class or interface type. This overload uses the specified <paramref name="typeComparer" />
Expand All @@ -206,11 +242,17 @@ public static bool InheritsFrom([ValidatedNotNull] this Type type, [ValidatedNot
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type" />, or <paramref name="baseClassOrInterfaceType" />, or <paramref name="typeComparer" /> is null.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ContractAnnotation("type:null => halt; baseClassOrInterfaceType:null => halt; typeComparer:null => halt")]
public static bool InheritsFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type baseClassOrInterfaceType, [ValidatedNotNull] IEqualityComparer<Type> typeComparer) =>
public static bool InheritsFrom(
#if NET8_0
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
[ValidatedNotNull] this Type type,
[ValidatedNotNull] Type baseClassOrInterfaceType,
[ValidatedNotNull] IEqualityComparer<Type> typeComparer) =>
baseClassOrInterfaceType.MustNotBeNull(nameof(baseClassOrInterfaceType))
.IsInterface
? type.Implements(baseClassOrInterfaceType, typeComparer)
: type.DerivesFrom(baseClassOrInterfaceType, typeComparer);
.IsInterface ?
type.Implements(baseClassOrInterfaceType, typeComparer) :
type.DerivesFrom(baseClassOrInterfaceType, typeComparer);

/// <summary>
/// Checks if the given <paramref name="type" /> is equal to the specified <paramref name="otherType" /> or if it derives from it or implements it.
Expand All @@ -222,7 +264,12 @@ public static bool InheritsFrom([ValidatedNotNull] this Type type, [ValidatedNot
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type" /> or <paramref name="otherType" /> is null.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ContractAnnotation("type:null => halt; otherType:null => halt")]
public static bool IsOrInheritsFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType) =>
public static bool IsOrInheritsFrom(
#if NET8_0
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
[ValidatedNotNull] this Type type,
[ValidatedNotNull] Type otherType) =>
type.IsEquivalentTypeTo(otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType);


Expand All @@ -236,7 +283,13 @@ public static bool IsOrInheritsFrom([ValidatedNotNull] this Type type, [Validate
/// <exception cref="ArgumentNullException">Thrown when <paramref name="type" /> or <paramref name="otherType" /> is null.</exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[ContractAnnotation("type:null => halt; otherType:null => halt; typeComparer:null => halt")]
public static bool IsOrInheritsFrom([ValidatedNotNull] this Type type, [ValidatedNotNull] Type otherType, [ValidatedNotNull] IEqualityComparer<Type> typeComparer) =>
public static bool IsOrInheritsFrom(
#if NET8_0
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
#endif
[ValidatedNotNull] this Type type,
[ValidatedNotNull] Type otherType,
[ValidatedNotNull] IEqualityComparer<Type> typeComparer) =>
typeComparer.MustNotBeNull(nameof(typeComparer)).Equals(type, otherType.MustNotBeNull(nameof(otherType))) || type.InheritsFrom(otherType, typeComparer);


Expand Down
16 changes: 6 additions & 10 deletions Code/Light.GuardClauses/EnumInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Light.GuardClauses;
/// Can be used to validate that an enum value is valid.
/// </summary>
/// <typeparam name="T">The type of the enum.</typeparam>
public static class EnumInfo<T> where T : Enum
public static class EnumInfo<T> where T : struct, Enum
{
// ReSharper disable StaticMemberInGenericType

Expand All @@ -28,23 +28,19 @@ public static class EnumInfo<T> where T : Enum
private static readonly int EnumSize = Unsafe.SizeOf<T>();
private static readonly T[] EnumConstantsArray;

/// <summary>
/// Gets the underlying type that is used for the enum (<see cref="int" /> for default enums).
/// </summary>
public static readonly Type UnderlyingType;

/// <summary>
/// Gets the values of the enum as a read-only collection.
/// </summary>
public static ReadOnlyMemory<T> EnumConstants { get; }

static EnumInfo()
{
#if NET8_0
EnumConstantsArray = Enum.GetValues<T>();
#else
EnumConstantsArray = (T[]) Enum.GetValues(typeof(T));
var fields = typeof(T).GetFields();

UnderlyingType = fields[0].FieldType;
EnumConstants = new ReadOnlyMemory<T>(EnumConstantsArray);
#endif
EnumConstants = new (EnumConstantsArray);

if (!IsFlagsEnum)
return;
Expand Down
2 changes: 2 additions & 0 deletions Code/Light.GuardClauses/Exceptions/AbsoluteUriException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class AbsoluteUriException : UriException
/// <param name="message">The message of the exception (optional).</param>
public AbsoluteUriException(string? parameterName = null, string? message = null) : base(parameterName, message) { }

#if !NET8_0
/// <inheritdoc />
protected AbsoluteUriException(SerializationInfo info, StreamingContext context) : base(info, context) { }
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class ArgumentDefaultException : ArgumentException
/// <param name="message">The message of the exception (optional).</param>
public ArgumentDefaultException(string? parameterName = null, string? message = null) : base(message, parameterName) { }

#if !NET8_0
/// <inheritdoc />
protected ArgumentDefaultException(SerializationInfo info, StreamingContext context) : base(info, context) { }
#endif
}
2 changes: 2 additions & 0 deletions Code/Light.GuardClauses/Exceptions/CollectionException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class CollectionException : ArgumentException
/// <param name="message">The message of the exception (optional).</param>
public CollectionException(string? parameterName = null, string? message = null) : base(message, parameterName) { }

#if !NET8_0
/// <inheritdoc />
protected CollectionException(SerializationInfo info, StreamingContext context) : base(info, context) { }
#endif
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public class EmptyCollectionException : InvalidCollectionCountException
/// <param name="message">The message of the exception (optional).</param>
public EmptyCollectionException(string? parameterName = null, string? message = null) : base(parameterName, message) { }

#if !NET8_0
/// <inheritdoc />
protected EmptyCollectionException(SerializationInfo info, StreamingContext context) : base(info, context) { }
#endif
}
Loading