Skip to content

[API Proposal]: Add ToLowerOrdinal() & ToUpperOrdinal() #90999

Description

@Neme12

Background and motivation

Since #32247/#40910, this code

string.Equals(a, b, StringComparison.OrdinalIgnoreCase)

is no longer equivalent to this code

a.ToUpperInvariant() == b.ToUpperInvariant()

That's why I'm proposing to add ToLowerOrdinal() and ToUpperOrdinal() methods that would be guaranteed to have the same semantics as OrdinalIgnoreCase comparisons, and that would also be guaranteed to be case folded letter-by-letter and have the same length as the input string (and IMO, ToLowerInvariant and ToUpperInvariant should be changed to use proper ICU case semantics that wouldn't have to result in the same length, as per #32247 (comment), but that's a separate discussion).

API Proposal

 namespace System;

 public sealed partial class String
 {
     public string ToLower();
     public string ToLower(CultureInfo? culture);
     public string ToLowerInvariant();
+    public string ToLowerOrdinal();
     public string ToUpper();
     public string ToUpper(CultureInfo? culture);
     public string ToUpperInvariant();
+    public string ToUpperOrdinal();
 }

 public readonly partial struct Char
 {
     public static char ToLower(char c);
     public static char ToLower(char c, CultureInfo culture);
     public static char ToLowerInvariant(char c);
+    public static char ToLowerOrdinal(char c);
     public static char ToUpper(char c);
     public static char ToUpper(char c, CultureInfo culture);
     public static char ToUpperInvariant(char c);
+    public static char ToUpperOrdinal(char c);
 }

 namespace System.Text;

 public readonly partial struct Rune
 {
     public static Rune ToLower(Rune value, CultureInfo culture);
     public static Rune ToLowerInvariant(Rune value);
+    public static Rune ToLowerOrdinal(Rune value);
     public static Rune ToUpper(Rune value, CultureInfo culture);
     public static Rune ToUpperInvariant(Rune value);
+    public static Rune ToUpperOrdinal(Rune value);
 }

 namespace System;

 public static partial class MemoryExtensions
 {
     public static int ToLower(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo? culture);
     public static int ToLowerInvariant(this ReadOnlySpan<char> source, Span<char> destination);
+    public static int ToLowerOrdinal(this ReadOnlySpan<char> source, Span<char> destination);
     public static int ToUpper(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo? culture);
     public static int ToUpperInvariant(this ReadOnlySpan<char> source, Span<char> destination);
+    public static int ToUpperOrdinal(this ReadOnlySpan<char> source, Span<char> destination);
 }

API Usage

...

Alternative Designs

No response

Risks

No response

cc @GrabYourPitchforks

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions