From 120d423443d21fb729f04781ffb41e77123351a4 Mon Sep 17 00:00:00 2001 From: Vladimir Pecanac Date: Mon, 14 Sep 2026 16:18:40 +0200 Subject: [PATCH] Update CaseInsensitiveSubstringSearch sample to .NET 10 Retarget all three projects from net7.0 to net10.0 and move the test and benchmark packages to their current stable versions. Make RegexIsMatch treat the search term as literal text with Regex.Escape and add RegexOptions.CultureInvariant, so a term holding regex metacharacters no longer matches the wrong strings or throws. Add SubstringSearchSemanticsTest pinning three facts: LinqStringEquals matches whole separator-delimited words rather than substrings, the escaped regex search agrees with String.Contains on metacharacter terms, and all four containment methods still match under a Turkish culture. --- .../BenchmarkRunner/BenchmarkRunner.csproj | 4 +- .../CaseInsensitiveSubstringSearch.csproj | 2 +- .../SubstringSearch.cs | 9 ++- .../Tests/SubstringSearchSemanticsTest.cs | 60 +++++++++++++++++++ .../Tests/Tests.csproj | 10 ++-- 5 files changed, 76 insertions(+), 9 deletions(-) create mode 100644 strings-csharp/CaseInsensitiveSubstringSearch/Tests/SubstringSearchSemanticsTest.cs diff --git a/strings-csharp/CaseInsensitiveSubstringSearch/BenchmarkRunner/BenchmarkRunner.csproj b/strings-csharp/CaseInsensitiveSubstringSearch/BenchmarkRunner/BenchmarkRunner.csproj index 083757afe1..46ec814a74 100644 --- a/strings-csharp/CaseInsensitiveSubstringSearch/BenchmarkRunner/BenchmarkRunner.csproj +++ b/strings-csharp/CaseInsensitiveSubstringSearch/BenchmarkRunner/BenchmarkRunner.csproj @@ -2,13 +2,13 @@ Exe - net7.0 + net10.0 enable enable - + diff --git a/strings-csharp/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch.csproj b/strings-csharp/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch.csproj index f02677bf64..dfb40caafc 100644 --- a/strings-csharp/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch.csproj +++ b/strings-csharp/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net10.0 enable enable diff --git a/strings-csharp/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch/SubstringSearch.cs b/strings-csharp/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch/SubstringSearch.cs index db8b1143a9..cda2b1cd08 100644 --- a/strings-csharp/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch/SubstringSearch.cs +++ b/strings-csharp/CaseInsensitiveSubstringSearch/CaseInsensitiveSubstringSearch/SubstringSearch.cs @@ -28,13 +28,20 @@ public static bool StringToUpperInvariant(string sourceString, string substringT } // Regular Expression Search + // Regex.Escape() treats the search term as literal text, so a term such as "c.de" or "z*" + // is not reinterpreted as a pattern and an unmatched bracket does not throw. + // RegexOptions.CultureInvariant keeps IgnoreCase off the current culture's casing rules. public static bool RegexIsMatch(string sourceString, string substringToSearch) { return Regex - .IsMatch(sourceString, substringToSearch, RegexOptions.IgnoreCase); + .IsMatch(sourceString, + Regex.Escape(substringToSearch), + RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } // Linq With String Equals Method Search + // This matches whole separator-delimited words, not substrings, so it answers a different + // question from the other four methods. That difference is intentional, not a bug. public static bool LinqStringEquals(string sourceString, string substringToSearch, char separator) { return sourceString diff --git a/strings-csharp/CaseInsensitiveSubstringSearch/Tests/SubstringSearchSemanticsTest.cs b/strings-csharp/CaseInsensitiveSubstringSearch/Tests/SubstringSearchSemanticsTest.cs new file mode 100644 index 0000000000..92daa4363a --- /dev/null +++ b/strings-csharp/CaseInsensitiveSubstringSearch/Tests/SubstringSearchSemanticsTest.cs @@ -0,0 +1,60 @@ +using System.Globalization; +using CaseInsensitiveSubstringSearch; + +namespace Tests +{ + [TestClass] + public class SubstringSearchSemanticsTest + { + private const string SourceString = "Code Maze"; + + [TestMethod] + [DataRow("aze")] + [DataRow("ode")] + [DataRow("e M")] + public void GivenMidWordSubstring_WhenComparingAllFiveMethods_ThenOnlyLinqStringEqualsReturnsFalse(string subStringToSearch) + { + Assert.IsTrue(SubstringSearch.StringContains(SourceString, subStringToSearch)); + Assert.IsTrue(SubstringSearch.StringIndexOf(SourceString, subStringToSearch)); + Assert.IsTrue(SubstringSearch.StringToUpperInvariant(SourceString, subStringToSearch)); + Assert.IsTrue(SubstringSearch.RegexIsMatch(SourceString, subStringToSearch)); + + Assert.IsFalse(SubstringSearch.LinqStringEquals(SourceString, subStringToSearch, ' ')); + } + + [TestMethod] + [DataRow("c.de")] + [DataRow("z*")] + [DataRow("m|q")] + [DataRow("(")] + public void GivenSearchTermHoldingRegexMetacharacters_WhenUsingRegexIsMatchMethod_ThenItAgreesWithStringContains(string subStringToSearch) + { + var expectedResult = SubstringSearch.StringContains(SourceString, subStringToSearch); + + var result = SubstringSearch.RegexIsMatch(SourceString, subStringToSearch); + + Assert.AreEqual(expectedResult, result); + Assert.IsFalse(result); + } + + [TestMethod] + public void GivenTurkishCulture_WhenSearchingForFileInsideFILE_ThenAllFourContainmentMethodsMatch() + { + var originalCulture = CultureInfo.CurrentCulture; + + try + { + CultureInfo.CurrentCulture = new CultureInfo("tr-TR"); + + Assert.IsTrue(SubstringSearch.StringContains("FILE", "file")); + Assert.IsTrue(SubstringSearch.StringIndexOf("FILE", "file")); + Assert.IsTrue(SubstringSearch.StringToUpperInvariant("FILE", "file")); + Assert.IsTrue(SubstringSearch.RegexIsMatch("FILE", "file")); + } + finally + { + CultureInfo.CurrentCulture = originalCulture; + } + } + } +} diff --git a/strings-csharp/CaseInsensitiveSubstringSearch/Tests/Tests.csproj b/strings-csharp/CaseInsensitiveSubstringSearch/Tests/Tests.csproj index fbf6af0b48..a75aead610 100644 --- a/strings-csharp/CaseInsensitiveSubstringSearch/Tests/Tests.csproj +++ b/strings-csharp/CaseInsensitiveSubstringSearch/Tests/Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net10.0 enable enable @@ -9,10 +9,10 @@ - - - - + + + +