Integrate .NET5+ string notes into the best practices article and other places#51829
Integrate .NET5+ string notes into the best practices article and other places#51829
Conversation
There was a problem hiding this comment.
Pull request overview
This PR migrates the standalone “.NET 5+ string comparison behavior changes” content into the main string-comparison best practices article, updates inbound links/navigation, and adds new runnable snippet projects for the new examples.
Changes:
- Remove
string-comparison-net-5-plus.md, add a redirect, and remove the TOC entry. - Expand
best-practices-strings.mdwith ICU vs NLS background, security-filtering guidance, collation-elements concepts, and default-comparison tables. - Update related ICU/breaking-change docs to point at the best practices article, and add new C#/VB snippet projects.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/standard/base-types/string-comparison-net-5-plus.md | Removes the now-redundant standalone article. |
| docs/standard/base-types/best-practices-strings.md | Integrates ICU/NLS behavioral notes, adds new examples, and adds default behavior tables. |
| docs/standard/base-types/snippets/best-practices-strings/csharp/icu-demo/icu-demo.csproj | Adds buildable C# snippet project for ICU/NLS currency example. |
| docs/standard/base-types/snippets/best-practices-strings/csharp/icu-demo/Program.cs | Adds C# ICU/NLS currency formatting sample. |
| docs/standard/base-types/snippets/best-practices-strings/csharp/security-filtering/security-filtering.csproj | Adds buildable C# snippet project for security-filtering example. |
| docs/standard/base-types/snippets/best-practices-strings/csharp/security-filtering/Program.cs | Adds C# security-filtering sample comparing default vs ordinal search. |
| docs/standard/base-types/snippets/best-practices-strings/csharp/collation-elements/collation-elements.csproj | Adds buildable C# snippet project for collation-elements example. |
| docs/standard/base-types/snippets/best-practices-strings/csharp/collation-elements/Program.cs | Adds C# collation-elements + Hungarian culture-aware sample. |
| docs/standard/base-types/snippets/best-practices-strings/vb/icu-demo/icu-demo.vbproj | Adds buildable VB snippet project for ICU/NLS currency example. |
| docs/standard/base-types/snippets/best-practices-strings/vb/icu-demo/Program.vb | Adds VB ICU/NLS currency formatting sample. |
| docs/standard/base-types/snippets/best-practices-strings/vb/security-filtering/security-filtering.vbproj | Adds buildable VB snippet project for security-filtering example. |
| docs/standard/base-types/snippets/best-practices-strings/vb/security-filtering/Program.vb | Adds VB security-filtering sample comparing default vs ordinal search. |
| docs/standard/base-types/snippets/best-practices-strings/vb/collation-elements/collation-elements.vbproj | Adds buildable VB snippet project for collation-elements example. |
| docs/standard/base-types/snippets/best-practices-strings/vb/collation-elements/Program.vb | Adds VB collation-elements + Hungarian culture-aware sample. |
| docs/fundamentals/toc.yml | Removes the removed-article entry from navigation. |
| docs/core/extensions/globalization-icu.md | Retargets “more information” link to the best practices article. |
| docs/core/compatibility/globalization/5.0/icu-globalization-api.md | Adds “See also” link to the best practices article and updates references. |
| docs/core/compatibility/globalization/7.0/icu-globalization-api.md | Adds “See also” link to the best practices article. |
| .openpublishing.redirection.standard.json | Adds redirect from the removed article to best-practices-strings. |
| <TargetFramework>net9.0</TargetFramework> | ||
| <RootNamespace>security_filtering</RootNamespace> |
There was a problem hiding this comment.
VB snippet projects in this directory consistently target net8.0 and enable OptionStrict (see .../vb/turkish/turkish.vbproj). This new project targets net9.0 and doesn't set OptionStrict, which makes the snippets inconsistent and could let type issues slip through. Consider matching the existing VB snippet project conventions in this directory.
| <TargetFramework>net9.0</TargetFramework> | |
| <RootNamespace>security_filtering</RootNamespace> | |
| <TargetFramework>net8.0</TargetFramework> | |
| <RootNamespace>security_filtering</RootNamespace> | |
| <OptionStrict>On</OptionStrict> |
| - **.NET** uses the [International Components for Unicode (ICU)](https://icu.unicode.org/) libraries for globalization functionality across all platforms (Windows, Linux, macOS). ICU is an industry-standard Unicode implementation that provides consistent behavior across operating systems. | ||
| - **.NET Framework** uses [National Language Support (NLS)](/windows/win32/intl/national-language-support) APIs on Windows, which is a Windows-specific globalization system. |
There was a problem hiding this comment.
This section states that .NET uses ICU “across all platforms (Windows, Linux, macOS)”. On Windows, .NET’s default globalization library depends on the OS version and runtime version (for example, ICU on Windows 10 19H1+ starting in .NET 5, ICU on Windows Server 2019 starting in .NET 7). Consider qualifying this statement (and/or linking to the ICU-on-Windows details) to avoid implying ICU is always the default on Windows.
| ' Demonstrate Unicode normalization with résumé | ||
| Console.WriteLine("=== Unicode Normalization Example ===") | ||
| Console.WriteLine("resume".IndexOf("e", StringComparison.Ordinal)) ' prints '1' | ||
| Console.WriteLine("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e", StringComparison.Ordinal) ' prints '-1' |
There was a problem hiding this comment.
Line 9 has mismatched parentheses, so .IndexOf(...) is being applied to the result of Console.WriteLine(...) instead of to the constructed string. This won't compile; wrap the concatenation in parentheses and call IndexOf before passing the result to Console.WriteLine.
| Console.WriteLine("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e", StringComparison.Ordinal) ' prints '-1' | |
| Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e", StringComparison.Ordinal)) ' prints '-1' |
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net9.0</TargetFramework> |
There was a problem hiding this comment.
These snippet projects in this folder consistently target net8.0 and enable common settings (for example, Nullable and ImplicitUsings) (see .../csharp/turkish/turkish.csproj). This new project targets net9.0 and omits those settings, which makes the snippets inconsistent and harder to maintain. Consider matching the existing snippet project conventions in this directory.
| <TargetFramework>net9.0</TargetFramework> | |
| <TargetFramework>net8.0</TargetFramework> | |
| <Nullable>enable</Nullable> | |
| <ImplicitUsings>enable</ImplicitUsings> |
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net9.0</TargetFramework> |
There was a problem hiding this comment.
These snippet projects in this folder consistently target net8.0 and enable common settings (for example, Nullable and ImplicitUsings) (see .../csharp/turkish/turkish.csproj). This new project targets net9.0 and omits those settings, which makes the snippets inconsistent and harder to maintain. Consider matching the existing snippet project conventions in this directory.
| <TargetFramework>net9.0</TargetFramework> | |
| <TargetFramework>net8.0</TargetFramework> | |
| <Nullable>enable</Nullable> | |
| <ImplicitUsings>enable</ImplicitUsings> |
| <TargetFramework>net9.0</TargetFramework> | ||
| <RootNamespace>icu_demo</RootNamespace> |
There was a problem hiding this comment.
VB snippet projects in this directory consistently target net8.0 and enable OptionStrict (see .../vb/turkish/turkish.vbproj). This new project targets net9.0 and doesn't set OptionStrict, which makes the snippets inconsistent and could let type issues slip through. Consider matching the existing VB snippet project conventions in this directory.
| <TargetFramework>net9.0</TargetFramework> | |
| <RootNamespace>icu_demo</RootNamespace> | |
| <TargetFramework>net8.0</TargetFramework> | |
| <RootNamespace>icu_demo</RootNamespace> | |
| <OptionStrict>On</OptionStrict> |
| <TargetFramework>net9.0</TargetFramework> | ||
| <RootNamespace>collation_elements</RootNamespace> |
There was a problem hiding this comment.
VB snippet projects in this directory consistently target net8.0 and enable OptionStrict (see .../vb/turkish/turkish.vbproj). This new project targets net9.0 and doesn't set OptionStrict, which makes the snippets inconsistent and could let type issues slip through. Consider matching the existing VB snippet project conventions in this directory.
| <TargetFramework>net9.0</TargetFramework> | |
| <RootNamespace>collation_elements</RootNamespace> | |
| <TargetFramework>net8.0</TargetFramework> | |
| <RootNamespace>collation_elements</RootNamespace> | |
| <OptionStrict>On</OptionStrict> |
| When running on .NET Framework, the output is `"100,00 €"` (using the euro symbol). On .NET, the output is `"100,00 ¤"` (using the international currency symbol). This difference occurs because ICU treats currency as a property of a country or region, not just a language, whereas the language-only German culture (`"de"`) doesn't specify a country. | ||
|
|
There was a problem hiding this comment.
The currency output difference described here is platform- and version-specific (it’s documented as a behavioral change on certain Windows versions when ICU is used). Consider qualifying “On .NET” to the specific conditions (for example, .NET 5+ on Windows 10 19H1+ / .NET 7+ on Windows Server 2019) so readers on other OSes or Windows versions don’t get confused by different output.
|
|
||
| When running on .NET Framework, the output is `"100,00 €"` (using the euro symbol). On .NET, the output is `"100,00 ¤"` (using the international currency symbol). This difference occurs because ICU treats currency as a property of a country or region, not just a language, whereas the language-only German culture (`"de"`) doesn't specify a country. | ||
|
|
||
| If your application requires the older NLS behavior when running on .NET, you can enable it through [runtime configuration](../../core/runtime-config/globalization.md#nls). However, for new applications, we recommend using explicit `StringComparison` parameters to make string comparison behavior clear and consistent. |
There was a problem hiding this comment.
To align with the docs writing style guidance (avoid “we” voice), consider rephrasing “we recommend using…” to a direct instruction (for example, “use explicit StringComparison parameters…”).
| If your application requires the older NLS behavior when running on .NET, you can enable it through [runtime configuration](../../core/runtime-config/globalization.md#nls). However, for new applications, we recommend using explicit `StringComparison` parameters to make string comparison behavior clear and consistent. | |
| If your application requires the older NLS behavior when running on .NET, you can enable it through [runtime configuration](../../core/runtime-config/globalization.md#nls). For new applications, use explicit `StringComparison` parameters to make string comparison behavior clear and consistent. |
| Console.WriteLine(vbCrLf & "=== Linguistic Comparison Example ===") | ||
| Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf("e")) ' prints '-1' (not found) | ||
| Console.WriteLine(("r" & ChrW(&HE9) & "sum" & ChrW(&HE9)).IndexOf(ChrW(&HE9))) ' prints '1' | ||
| Console.WriteLine(ChrW(&HE9).IndexOf("e" & ChrW(&H301))) ' prints '0' |
There was a problem hiding this comment.
ChrW(&HE9) returns a Char, and Char doesn't have an IndexOf method. Convert it to a String (for example, via .ToString()) before calling IndexOf so the snippet compiles.
| Console.WriteLine(ChrW(&HE9).IndexOf("e" & ChrW(&H301))) ' prints '0' | |
| Console.WriteLine(ChrW(&HE9).ToString().IndexOf("e" & ChrW(&H301))) ' prints '0' |
BillWagner
left a comment
There was a problem hiding this comment.
I had a couple small comments, but this is ready to merge.
|
|
||
| <PropertyGroup> | ||
| <OutputType>Exe</OutputType> | ||
| <TargetFramework>net9.0</TargetFramework> |
There was a problem hiding this comment.
Or do you want to upgrade to .NET 10?
|
Yeah. The code was just moved around, I didn't go update it. So I'll do that now! |
Summary
Fixes #50673
Internal previews