From 87d5a745b253a34ccbb7aa1c8c90c9c6fad9523e Mon Sep 17 00:00:00 2001 From: Sebastian Szvetecz Date: Sun, 16 Aug 2026 12:42:22 +0200 Subject: [PATCH 1/2] Add Guard.IsDefined for enum validation Introduce Guard.IsDefined for efficient enum value checks using cached sets. Add EnumCache and ThrowHelper for fast lookups and detailed exceptions. Update csproj to refine package references and add System.Collections.Immutable for .NET Standard. --- .../CommunityToolkit.Diagnostics.csproj | 24 ++++----- .../Guard.Enum.cs | 51 +++++++++++++++++++ .../Internals/Guard.Enum.ThrowHelper.cs | 26 ++++++++++ 3 files changed, 88 insertions(+), 13 deletions(-) create mode 100644 src/CommunityToolkit.Diagnostics/Guard.Enum.cs create mode 100644 src/CommunityToolkit.Diagnostics/Internals/Guard.Enum.ThrowHelper.cs diff --git a/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj b/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj index 870c8c55b..425cf15b1 100644 --- a/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj +++ b/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj @@ -14,22 +14,20 @@ Diagnostics;Guard;ThrowHelper;TypeInfo;Extensions;Helpers - - + - - - - - + + + - - - - - - + + + + + + + diff --git a/src/CommunityToolkit.Diagnostics/Guard.Enum.cs b/src/CommunityToolkit.Diagnostics/Guard.Enum.cs new file mode 100644 index 000000000..7252068c4 --- /dev/null +++ b/src/CommunityToolkit.Diagnostics/Guard.Enum.cs @@ -0,0 +1,51 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Frozen; +using System.Runtime.CompilerServices; + +namespace CommunityToolkit.Diagnostics; + +/// +public static partial class Guard +{ + /// + /// Asserts that the input value is defined in the specified enumeration. + /// + /// The type of enumeration to validate. + /// The input to test. + /// The name of the input parameter being tested. + /// Thrown if is not defined in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void IsDefined(TEnum value, [CallerArgumentExpression(nameof(value))] string name = "") + where TEnum : struct, Enum + { + if (EnumCache.Values.Contains(value)) + { + return; + } + + ThrowHelper.ThrowArgumentExceptionForEnumNotDefined(value, name); + } +} + +/// +/// Provides a cached set of all defined values for a specific enumeration type. +/// +/// The type of enumeration whose defined values are cached. +internal static class EnumCache where TEnum : struct, Enum +{ + static EnumCache() + { +#if NET5_0_OR_GREATER + TEnum[] values = Enum.GetValues(); +#else + TEnum[] values = (TEnum[])Enum.GetValues(typeof(TEnum)); +#endif + Values = values.ToFrozenSet(); + } + + public static readonly FrozenSet Values; +} diff --git a/src/CommunityToolkit.Diagnostics/Internals/Guard.Enum.ThrowHelper.cs b/src/CommunityToolkit.Diagnostics/Internals/Guard.Enum.ThrowHelper.cs new file mode 100644 index 000000000..0eebc72b8 --- /dev/null +++ b/src/CommunityToolkit.Diagnostics/Internals/Guard.Enum.ThrowHelper.cs @@ -0,0 +1,26 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace CommunityToolkit.Diagnostics; + +/// +partial class Guard +{ + /// + partial class ThrowHelper + { + /// + /// Throws an when an enum value is not defined. + /// + [DoesNotReturn] + public static void ThrowArgumentExceptionForEnumNotDefined(TEnum value, string name) + where TEnum : struct, Enum + { + throw new ArgumentException($"The value of enum {typeof(TEnum).Name} ({AssertString(value)}) is not defined.", name); + } + } +} From de4dad9c1c6b527592051e00f98f8f610f5473a2 Mon Sep 17 00:00:00 2001 From: Sebastian Szvetecz Date: Sun, 16 Aug 2026 12:43:21 +0200 Subject: [PATCH 2/2] Add enum tests; update internals Added Test_Guard.Enum.cs. Updated AssemblyInfo.cs with InternalsVisibleTo for unit tests. Introduced Test_Guard.Enum.cs with unit tests for EnumCache and Guard.IsDefined, including a new file-scoped TestEnum. --- .../Properties/AssemblyInfo.cs | 7 +++ .../Test_Guard.Enum.cs | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/CommunityToolkit.Diagnostics/Properties/AssemblyInfo.cs create mode 100644 tests/CommunityToolkit.Diagnostics.UnitTests/Test_Guard.Enum.cs diff --git a/src/CommunityToolkit.Diagnostics/Properties/AssemblyInfo.cs b/src/CommunityToolkit.Diagnostics/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..f012093ac --- /dev/null +++ b/src/CommunityToolkit.Diagnostics/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("CommunityToolkit.Diagnostics.UnitTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010041753af735ae6140c9508567666c51c6ab929806adb0d210694b30ab142a060237bc741f9682e7d8d4310364b4bba4ee89cc9d3d5ce7e5583587e8ea44dca09977996582875e71fb54fa7b170798d853d5d8010b07219633bdb761d01ac924da44576d6180cdceae537973982bb461c541541d58417a3794e34f45e6f2d129e2")] \ No newline at end of file diff --git a/tests/CommunityToolkit.Diagnostics.UnitTests/Test_Guard.Enum.cs b/tests/CommunityToolkit.Diagnostics.UnitTests/Test_Guard.Enum.cs new file mode 100644 index 000000000..e1637f550 --- /dev/null +++ b/tests/CommunityToolkit.Diagnostics.UnitTests/Test_Guard.Enum.cs @@ -0,0 +1,54 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; + +namespace CommunityToolkit.Diagnostics.UnitTests; + +public partial class Test_Guard +{ + [TestMethod] + public void Test_EnumCache_Initialization_Ok() + { + TestEnum[] values = [.. EnumCache.Values]; + TestEnum[] comparisonValues = [ + TestEnum.Value0, + TestEnum.Value1, + TestEnum.Value2, + TestEnum.Value3, + TestEnum.Value4, + ]; + + for (int i = 0; i < 5; i++) + { + Assert.AreEqual(values[i], comparisonValues[i]); + } + } + + [TestMethod] + public void Test_Guard_IsDefined_Ok() + { + Guard.IsDefined(TestEnum.Value0, nameof(Test_Guard_IsDefined_Ok)); + Guard.IsDefined(TestEnum.Value1, nameof(Test_Guard_IsDefined_Ok)); + Guard.IsDefined(TestEnum.Value2, nameof(Test_Guard_IsDefined_Ok)); + Guard.IsDefined(TestEnum.Value3, nameof(Test_Guard_IsDefined_Ok)); + Guard.IsDefined(TestEnum.Value4, nameof(Test_Guard_IsDefined_Ok)); + } + + [TestMethod] + public void Test_Guard_IsDefined_Fail() + { + _ = Assert.ThrowsExactly(() => Guard.IsDefined((TestEnum)10, nameof(Test_Guard_IsDefined_Fail))); + } +} + +file enum TestEnum +{ + Value0, + Value1, + Value2, + Value3, + Value4, +}