Skip to content

Flow System.Text.Rune through more APIs #27912

Closed

Description

@GrabYourPitchforks

System.Text.Rune was recently introduced (see dotnet/coreclr#20935) to make it easier for applications to work with Unicode text. We should do a pass of our APIs which currently take or return System.Char instances and enlighten them to understand System.Text.Rune. Here's an initial proposal that can serve as a first pass.

namespace System
{
    // Adds overloads to existing char-consuming methods
    // TODO: Do we need overloads of MemoryExtensions methods? May cause overload explosion.
    // If we had such overloads, they'd basically look like the below, but with a "this ROS<char>" parameter.
    public partial sealed class String
    {
        public bool Contains(Rune value);
        public bool Contains(Rune value, StringComparison comparisonType);

        public bool EndsWith(Rune value);
        public bool EndsWith(Rune value, StringComparison comparisonType);
        public bool EndsWith(char value, StringComparison comparisonType); // missing overload

        public int IndexOf(Rune value);
        public int IndexOf(Rune value, int startIndex);
        public int IndexOf(Rune value, int startIndex, int count);
        public int IndexOf(Rune value, StringComparison comparisonType);

        // no IndexOfAny overloads for now due to complexity of implementation, can add later if there's demand
        // no Join overloads for now, can add later if there's demand

        public int LastIndexOf(Rune value);
        public int LastIndexOf(Rune value, int startIndex);
        public int LastIndexOf(Rune value, int startIndex, int count);
        public int LastIndexOf(Rune value, StringComparison comparisonType);

        public string Replace(Rune oldRune, Rune newRune);

        public string[] Split(Rune separator, int count, StringSplitOptions options = default);
        public string[] Split(Rune separator, StringSplitOptions options = default);

        public bool StartsWith(Rune value);
        public bool StartsWith(Rune value, StringComparison comparisonType);
        public bool StartsWith(char value, StringComparison comparisonType); // missing overload

        // no Trim*(Rune[]) overloads for now due to complexity of implementation, can add later if there's demand
        public string Trim(Rune trimRune);
        public string TrimEnd(Rune trimRune);
        public string TrimStart(Rune trimRune);
    }
}

namespace System.Text
{
    // New methods on newly-introduced type
    public partial struct Rune
    {
        public static bool Equals(Rune left, Rune right); // mimics (left == right), ordinal
        public static bool Equals(Rune left, Rune right, StringComparison comparisonType); // for any non-ordinal comparison
    }

    // Adds overloads to existing char-consuming methods
    // Not all methods get overloads (e.g., AppendJoin) due to perceived rare usage and complexity of implementation
    public partial sealed class StringBuilder
    {
        public StringBuilder Append(Rune value);

        public Rune GetRuneAt(int index); // new method to match String, not an overload of an existing method

        public StringBuilder Insert(int index, Rune value);

        public StringBuilder Replace(Rune oldRune, Rune newRune);
        public StringBuilder Replace(Rune oldRune, Rune newRune, int startIndex, int count);

        public bool TryGetRuneAt(int index, out Rune value); // new method, not overload of existing method
    }
}

namespace System.IO
{
    // Adds overloads to existing char-consuming methods
    public partial abstract class TextWriter
    {
        public virtual void Write(Rune value);

        public virtual Task WriteAsync(Rune value);

        public virtual void WriteLine(Rune value);

        public virtual Task WriteLineAsync(Rune value);
    }
}

namespace System.Globalization
{
    public partial class TextInfo
    {
        public Rune ToLower(Rune r);
        public Rune ToUpper(Rune r);
    }
}

Activity

  1. GrabYourPitchforks commented on Nov 15, 2018

    @GrabYourPitchforks
    MemberAuthor

    I propose the String.DoSomething(Rune value, ...) APIs that do not take a StringComparison parameter default to ordinal. This matches the behavior that the String.DoSomething(char value, ...) APIs have (but does not necessarily match the behavior of String.DoSomething(string value, ...)).

    Example:

    bool String.StartsWith(string value); // existing method - uses CurrentCulture
    bool String.StartsWith(char value); // existing method - uses Ordinal
    bool String.StartsWith(Rune value); // proposed method - should use Ordinal to match 'char' overload behavior
  2. terrajobst commented on Nov 20, 2018

    @terrajobst
    Contributor

    Rune:

    • Let's make the Equals methods instance methods (first one is already there).

    StringBuilder:

    • Expose an enumerator EnumeratorRunes() (or whatever string does).

    TextInfo

    • Change r to value

    TextWriter

    • Consider return ValueTask for the async ones. Probabably not, because it shifts the onus on the implementer.
  3. danmoseley commented on Jun 10, 2019

    @danmoseley
    Contributor

    Moving to Future as this need not happen for 3.0 as these changes are additive.

  4. transferred this issue fromdotnet/corefxon Jan 31, 2020
  5. added this to the Future milestone on Jan 31, 2020
  6. removed
    untriagedNew issue has not been triaged by the area owner
    on Mar 3, 2020
  7. added
    blockingMarks issues that we want to fast track in order to unblock other important work
    and removed
    api-approvedAPI was approved in API review, it can be implemented
    on Apr 15, 2020
  8. modified the milestones: Future, 5.0 on Apr 15, 2020
  9. 29 remaining items

  10. modified the milestones: Future, 11.0.0 on Sep 23, 2025
  11. added a commit that references this issue on Sep 26, 2025
  12. tarekgh commented on Sep 26, 2025

    @tarekgh
    Member

    Done through the PR #117168

  13. added 2 commits that reference this issue on Sep 26, 2025
  14. jkotas commented on Sep 26, 2025

    @jkotas
    Member

    The PR was reverted

  15. reopened this on Sep 26, 2025
  16. Joy-less commented on Sep 26, 2025

    @Joy-less
    Contributor

    The PR was reverted

    Please see #120145. The PR should be un-reverted now that the fixes are available.

  17. added a commit that references this issue on Sep 26, 2025
  18. added a commit that references this issue on Sep 27, 2025
  19. tarekgh commented on Sep 27, 2025

    @tarekgh
    Member

    Fixed by #120145

  20. locked and limited conversation to collaborators on Oct 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions