From 595f3bc654412478fe56229a97cad71136397012 Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Sun, 11 Oct 2026 12:20:50 +0200 Subject: [PATCH 1/2] Validate a GUID in C#: .NET 10, null-safe validators, nullable enabled - Retarget both projects from net8.0 to net10.0 - Enable nullable reference types in both projects - Take string? in all six GuidHelper validators; the Parse, ParseExact and constructor helpers now also catch ArgumentNullException, and the regex helper returns false for null instead of throwing - Move the benchmark class from HowToUseStringPool.Benchmark to HowToValidateGuid.Benchmark - Initialise the test fixture at its declaration (CS8618) - Add a null-input test for each validator and X-format tests for ParseExact and TryParseExact (18 to 26 tests) - BenchmarkDotNet 0.15.8, Microsoft.NET.Test.Sdk 18.10.1, MSTest.TestAdapter and MSTest.TestFramework 4.5.1, coverlet.collector 10.1.0, AutoFixture 4.18.1 --- .../Benchmark/HowToValidateGuidBenchmark.cs | 2 +- .../HowToValidateGuid/GuidHelper.cs | 26 +++---- .../HowToValidateGuid.csproj | 6 +- .../HowToValidateGuid/Program.cs | 2 +- .../HowToValidateGuidTests/GuidHelperTests.cs | 70 ++++++++++++++++++- .../HowToValidateGuidTests.csproj | 14 ++-- 6 files changed, 92 insertions(+), 28 deletions(-) diff --git a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/Benchmark/HowToValidateGuidBenchmark.cs b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/Benchmark/HowToValidateGuidBenchmark.cs index 46455e3d2e..a1eae8bb2a 100644 --- a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/Benchmark/HowToValidateGuidBenchmark.cs +++ b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/Benchmark/HowToValidateGuidBenchmark.cs @@ -4,7 +4,7 @@ using BenchmarkDotNet.Order; using HowToValidateGuid; -namespace HowToUseStringPool.Benchmark; +namespace HowToValidateGuid.Benchmark; [Orderer(SummaryOrderPolicy.FastestToSlowest)] [GroupBenchmarksBy(BenchmarkLogicalGroupRule.ByParams)] diff --git a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/GuidHelper.cs b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/GuidHelper.cs index dc8dc38f6e..4dd07f093e 100644 --- a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/GuidHelper.cs +++ b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/GuidHelper.cs @@ -7,18 +7,18 @@ public partial class GuidHelper [GeneratedRegex("^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$")] private static partial Regex GuidValidatorRegex(); - public static bool ValidateWithRegex(string input) + public static bool ValidateWithRegex(string? input) { - return GuidValidatorRegex().IsMatch(input); + return input is not null && GuidValidatorRegex().IsMatch(input); } - public static bool ValidateWithGuidParse(string input) + public static bool ValidateWithGuidParse(string? input) { try { - Guid.Parse(input); + Guid.Parse(input!); } - catch (FormatException) + catch (Exception ex) when (ex is FormatException or ArgumentNullException) { return false; } @@ -26,13 +26,13 @@ public static bool ValidateWithGuidParse(string input) return true; } - public static bool ValidateWithGuidParseExact(string input, string format) + public static bool ValidateWithGuidParseExact(string? input, string format) { try { - Guid.ParseExact(input, format); + Guid.ParseExact(input!, format); } - catch (FormatException) + catch (Exception ex) when (ex is FormatException or ArgumentNullException) { return false; } @@ -40,23 +40,23 @@ public static bool ValidateWithGuidParseExact(string input, string format) return true; } - public static bool ValidateWithGuidTryParse(string input) + public static bool ValidateWithGuidTryParse(string? input) { return Guid.TryParse(input, out Guid _); } - public static bool ValidateWithGuidTryParseExact(string input, string format) + public static bool ValidateWithGuidTryParseExact(string? input, string format) { return Guid.TryParseExact(input, format, out Guid _); } - public static bool ValidateWithNewGuid(string input) + public static bool ValidateWithNewGuid(string? input) { try { - var _ = new Guid(input); + var _ = new Guid(input!); } - catch (FormatException) + catch (Exception ex) when (ex is FormatException or ArgumentNullException) { return false; } diff --git a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/HowToValidateGuid.csproj b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/HowToValidateGuid.csproj index 2b8181b980..c96a8eb1e7 100644 --- a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/HowToValidateGuid.csproj +++ b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/HowToValidateGuid.csproj @@ -2,13 +2,13 @@ Exe - net8.0 + net10.0 enable - disable + enable - + diff --git a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/Program.cs b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/Program.cs index 1c19c874ac..7b59902989 100644 --- a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/Program.cs +++ b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/Program.cs @@ -1,5 +1,5 @@ using BenchmarkDotNet.Running; -using HowToUseStringPool.Benchmark; +using HowToValidateGuid.Benchmark; using HowToValidateGuid; BenchmarkRunner.Run(); diff --git a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/GuidHelperTests.cs b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/GuidHelperTests.cs index 85b036f5b7..41c629db45 100644 --- a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/GuidHelperTests.cs +++ b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/GuidHelperTests.cs @@ -6,7 +6,7 @@ namespace HowToValidateGuidTests [TestClass] public class GuidHelperTests { - private IFixture _fixture; + private IFixture _fixture = new Fixture(); private const string formatKey = "D"; [TestInitialize] @@ -41,6 +41,14 @@ public void WhenValidateWithRegexCalled_ForAEmptyString_ThenResultMustBeFalse() Validate(input, GuidHelper.ValidateWithRegex, false); } + [TestMethod] + public void WhenValidateWithRegexCalled_ForANullString_ThenResultMustBeFalse() + { + string? input = null; + + Validate(input, GuidHelper.ValidateWithRegex, false); + } + #endregion #region ValidateWithGuidParse @@ -69,6 +77,14 @@ public void WhenValidateWithGuidParseCalled_ForAEmptyString_ThenResultMustBeFals Validate(input, GuidHelper.ValidateWithGuidParse, false); } + [TestMethod] + public void WhenValidateWithGuidParseCalled_ForANullString_ThenResultMustBeFalse() + { + string? input = null; + + Validate(input, GuidHelper.ValidateWithGuidParse, false); + } + #endregion #region ValidateWithGuidParseExact @@ -97,6 +113,22 @@ public void WhenValidateWithGuidParseExactCalled_ForAEmptyString_ThenResultMustB ValidateFormat(input, formatKey, GuidHelper.ValidateWithGuidParseExact, false); } + [TestMethod] + public void WhenValidateWithGuidParseExactCalled_ForANullString_ThenResultMustBeFalse() + { + string? input = null; + + ValidateFormat(input, formatKey, GuidHelper.ValidateWithGuidParseExact, false); + } + + [TestMethod] + public void WhenValidateWithGuidParseExactCalled_ForAGuidStringInXFormat_ThenResultMustBeTrue() + { + var input = _fixture.Create().ToString("X"); + + ValidateFormat(input, "X", GuidHelper.ValidateWithGuidParseExact, true); + } + #endregion #region ValidateWithGuidTryParse @@ -125,6 +157,14 @@ public void WhenValidateWithGuidTryParseCalled_ForAEmptyString_ThenResultMustBeF Validate(input, GuidHelper.ValidateWithGuidTryParse, false); } + [TestMethod] + public void WhenValidateWithGuidTryParseCalled_ForANullString_ThenResultMustBeFalse() + { + string? input = null; + + Validate(input, GuidHelper.ValidateWithGuidTryParse, false); + } + #endregion #region ValidateWithGuidTryParseExact @@ -153,6 +193,22 @@ public void WhenValidateWithGuidTryParseExactCalled_ForAEmptyString_ThenResultMu ValidateFormat(input, formatKey, GuidHelper.ValidateWithGuidTryParseExact, false); } + [TestMethod] + public void WhenValidateWithGuidTryParseExactCalled_ForANullString_ThenResultMustBeFalse() + { + string? input = null; + + ValidateFormat(input, formatKey, GuidHelper.ValidateWithGuidTryParseExact, false); + } + + [TestMethod] + public void WhenValidateWithGuidTryParseExactCalled_ForAGuidStringInXFormat_ThenResultMustBeTrue() + { + var input = _fixture.Create().ToString("X"); + + ValidateFormat(input, "X", GuidHelper.ValidateWithGuidTryParseExact, true); + } + #endregion #region ValidateWithNewGuid @@ -181,16 +237,24 @@ public void WhenValidateWithNewGuidCalled_ForAEmptyString_ThenResultMustBeFalse( Validate(input, GuidHelper.ValidateWithNewGuid, false); } + [TestMethod] + public void WhenValidateWithNewGuidCalled_ForANullString_ThenResultMustBeFalse() + { + string? input = null; + + Validate(input, GuidHelper.ValidateWithNewGuid, false); + } + #endregion - private void Validate(string input, Func action, bool expected) + private void Validate(string? input, Func action, bool expected) { var result = action(input); Assert.AreEqual(expected, result); } - private void ValidateFormat(string input, string format, Func action, bool expected) + private void ValidateFormat(string? input, string format, Func action, bool expected) { var result = action(input, format); diff --git a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/HowToValidateGuidTests.csproj b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/HowToValidateGuidTests.csproj index ef9dbc677a..2c39eee1fe 100644 --- a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/HowToValidateGuidTests.csproj +++ b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/HowToValidateGuidTests.csproj @@ -1,20 +1,20 @@  - net8.0 + net10.0 enable - disable + enable false true - - - - - + + + + + From dfdcc46393a07c32941d1b88da52b0972ac0bc3f Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Sun, 11 Oct 2026 12:53:15 +0200 Subject: [PATCH 2/2] Add IsUsableGuid to GuidHelper with tests - IsUsableGuid returns true only for a string that parses as a GUID and is not Guid.Empty, as the article shows it - Four tests: null, a random string and Guid.Empty return false, a real GUID returns true (26 to 30 tests) --- .../HowToValidateGuid/GuidHelper.cs | 3 ++ .../HowToValidateGuidTests/GuidHelperTests.cs | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/GuidHelper.cs b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/GuidHelper.cs index 4dd07f093e..9769cef395 100644 --- a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/GuidHelper.cs +++ b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuid/GuidHelper.cs @@ -63,4 +63,7 @@ public static bool ValidateWithNewGuid(string? input) return true; } + + public static bool IsUsableGuid(string? input) => + Guid.TryParse(input, out var id) && id != Guid.Empty; } diff --git a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/GuidHelperTests.cs b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/GuidHelperTests.cs index 41c629db45..186221e43f 100644 --- a/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/GuidHelperTests.cs +++ b/csharp-classes-struct-record/HowToValidateGuid/HowToValidateGuidTests/GuidHelperTests.cs @@ -247,6 +247,42 @@ public void WhenValidateWithNewGuidCalled_ForANullString_ThenResultMustBeFalse() #endregion + #region IsUsableGuid + + [TestMethod] + public void WhenIsUsableGuidCalled_ForANullString_ThenResultMustBeFalse() + { + string? input = null; + + Validate(input, GuidHelper.IsUsableGuid, false); + } + + [TestMethod] + public void WhenIsUsableGuidCalled_ForARandomString_ThenResultMustBeFalse() + { + var input = _fixture.Create() + _fixture.Create(); + + Validate(input, GuidHelper.IsUsableGuid, false); + } + + [TestMethod] + public void WhenIsUsableGuidCalled_ForAnEmptyGuidString_ThenResultMustBeFalse() + { + var input = Guid.Empty.ToString(); + + Validate(input, GuidHelper.IsUsableGuid, false); + } + + [TestMethod] + public void WhenIsUsableGuidCalled_ForAGuidString_ThenResultMustBeTrue() + { + var input = _fixture.Create().ToString(); + + Validate(input, GuidHelper.IsUsableGuid, true); + } + + #endregion + private void Validate(string? input, Func action, bool expected) { var result = action(input);