Skip to content

First letter to upper case: retarget net10.0, source-generated regex, tests that can fail - #2215

Open
vladimir-pecanac-main wants to merge 1 commit into
CodeMazeBlog:mainfrom
vladimir-pecanac-main:seo/75793-csharp-first-letter-upper-case
Open

vladimir-pecanac-main wants to merge 1 commit into
CodeMazeBlog:mainfrom
vladimir-pecanac-main:seo/75793-csharp-first-letter-upper-case

Conversation

@vladimir-pecanac-main

Copy link
Copy Markdown
Collaborator

Retargets strings-csharp/FirstLetterToUpper from net6.0 to net10.0 and brings the sample up to date with the article rewrite.

Packages (queried on NuGet 2026-09-15): BenchmarkDotNet and BenchmarkDotNet.Annotations 0.13.2 to 0.15.8, Microsoft.NET.Test.Sdk 17.1.0 to 18.10.1, MSTest.TestAdapter and MSTest.TestFramework 2.2.8 to 4.4.0, coverlet.collector 3.1.2 to 10.0.1. The two-major MSTest jump costs this folder nothing: there is no Assert.ThrowsException call site here, and the existing [TestClass] and [TestMethod] usage compiles unchanged. 4.4.0 is the highest version that builds green.

Regex. FirstCharToUpperRegex moves to a [GeneratedRegex("^\p{Ll}")] partial method. The old "^[a-z]" matched ASCII only, so a string starting with a non-ASCII lowercase letter came back unchanged while every other method in the class capitalized it. A test covers that case now.

Unsafe method. The body stays, because it is the subject of a section of the article. It gains an XML doc comment and an inline comment stating that it mutates the instance it was handed, so an interned literal changes for every reference to it.

The tests could not fail. All eight declared the same literal "this is a test string", which is one interned instance shared across the class, so once the unsafe test ran, every later test received an already capitalized input. The assertions only checked char.IsUpper(returnedString[0]). They now assert the whole expected string, the unsafe tests build their own non-interned instances with new string(...ToCharArray()), and two tests are added: one proving the mutation reaches a second variable holding the same string, one for the non-ASCII regex case.

Idiom lift: file-scoped namespaces in both projects, and private readonly FirstLetterToUpperMethods _upperCase = new(); in the test class.

Build: 0 warnings, 0 errors. Tests: 10 of 10 passing on SDK 10.0.302, runtime .NET 10.0.10. The benchmark was re-run on the retargeted project and its output goes into the article.

… tests that can fail

Retarget both projects from net6.0 to net10.0 and move the packages to current
versions: BenchmarkDotNet and BenchmarkDotNet.Annotations 0.15.8,
Microsoft.NET.Test.Sdk 18.10.1, MSTest.TestAdapter and MSTest.TestFramework 4.4.0
(from 2.2.8), coverlet.collector 10.0.1. No source change was needed for the MSTest
major jump: this file has no Assert.ThrowsException call site.

FirstCharToUpperRegex moves from Regex.Replace(input, "^[a-z]", ...) to a
[GeneratedRegex("^\p{Ll}")] partial method. The ASCII class silently passed through
any non-ASCII first letter that every other method here capitalizes.

FirstCharToUpperUnsafeCode keeps its body and gains an XML doc comment and an inline
comment saying what it really does: it mutates the instance it was given, so an
interned literal changes for every reference to it. It is demonstration code.

The unit tests could not fail before. All eight shared one interned literal, so the
unsafe test upper-cased the input of every test that ran after it, and every
assertion only checked char.IsUpper on the first character. The assertions are now
AreEqual on the whole expected string, the unsafe tests build their own non-interned
instances, and two tests are added: one proving the mutation reaches a second
variable, one covering a non-ASCII first letter.

Idiom lift: file-scoped namespaces, and the test class field becomes
private readonly FirstLetterToUpperMethods _upperCase = new().

Build 0 warnings 0 errors, 10 of 10 tests passing on SDK 10.0.302, runtime 10.0.10.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant