次の方法で共有


String.Trim*(params ReadOnlySpan<char>) オーバーロードが削除されました

.NET エコシステムでは、 ReadOnlySpan<char> は次を表すことができます。

  • 文字の特定のシーケンス 。多くの場合、より大きな System.String インスタンスのスライスとして使用されます。
  • 1 文字のコレクション。多くの場合、 char[]のスライスとして使用されます。

.NET 9 の以前のリリースでは、params T[] オーバーロードが既に存在するメソッド グループにparams ReadOnlySpan<T>オーバーロードが追加されました。 このオーバーロードは一部のメソッド グループに対して肯定的な追加でしたが、 ReadOnlySpan<char> の二重の性質により、 char[]String (同じ位置) を受け入れ、それらが異なる方法で処理されるメソッド グループに混乱を引き起こす可能性があります。 たとえば、 public static string [String::]Split(string separator, StringSplitOptions options) は文字のシーケンスを 1 つの区切り文字と見なします。 たとえば、 "[]ne]-[Tw[]".Split("]-[", StringSplitOptions.None)new string[] { "[]ne", "Tw[]" };に分割されます。 一方、 public static [String::]Split(char[] separator, StringSplitOptions options) では、 separator 内の各文字が個別の区切り文字と見なされるため、配列と同等の分割によって new string[] { "", "", "ne", "", "", "Tw", "", "" }が生成されます。 したがって、 ReadOnlySpan<char> を受け入れる新しいオーバーロードは、文字列に似ているか配列に似ているかを判断する必要があります。 一般に、.NET は配列に似た動作に準拠しています。

dotnet/runtime#77873 で提案されているように、ReadOnlySpan<char>引数を受け取る次の新しいStringオーバーロードについて考。

public string[] Split(params ReadOnlySpan<char> separator);
public string Trim(params ReadOnlySpan<char> trimChars);
public string TrimStart(params ReadOnlySpan<char> trimChars);
public string TrimEnd(params ReadOnlySpan<char> trimChars);

さらに、次の一般的に定義されている拡張メソッドを検討してください。

public static class SomeExtensions {
    public static string TrimEnd(this string target, string trimString) {
        if (target.EndsWith(trimString) {
            return target.Substring(0, target.Length - trimString.Length);
        }

        return target;
    }
}

既存の .NET ランタイムの場合、この拡張メソッドは、指定したシーケンスを文字列の末尾から削除します。 ただし、C# のオーバーロード解決規則により、 "12345!!!!".TrimEnd("!!!") は既存の拡張メソッドよりも新しい TrimEnd オーバーロードを優先し、結果を "12345!" (3 つの感嘆符の完全なセットのみを削除) から "12345" (末尾からすべての感嘆符を削除) に変更します。

この中断を解決するには、さらに優れたターゲットであるインスタンス メソッド public string TrimEnd(string trimString) を導入するか、新しいメソッドを削除するという 2 つのパスが考えられます。 最初のオプションは、ターゲット文字列の 1 つのインスタンスを返すか、それらのすべてを返すかを決定する必要があるため、追加のリスクを伴います。 そして、各アプローチを使用する既存のコードを持つ呼び出し元は間違いなく存在します。 したがって、2 番目のオプションは、リリース サイクルのこのステージに最も適した選択肢でした。

str.Trim(';', ',', '.')など、params機能を使用して個々の文字を渡すString.Trimの呼び出し元には、中断は表示されません。 コードは、 string.Trim(params char[]) の呼び出しから string.Trim(params ReadOnlySpan<char>)に自動的に切り替わります。 .NET 9 の GA リリースに対してリビルドすると、コンパイラは自動的に char[] オーバーロードに戻ります。

ReadOnlySpan<char>を明示的に渡すString.Trimの呼び出し元 (または、char[]に変換できないReadOnlySpan<char>に変換できる型) は、この変更後にTrimを正常に呼び出すようにコードを変更する必要があります。

String.Splitに関しては、String.Trimとは異なり、このメソッドには既にオーバーロードがあります。これは単一の文字列パラメーターを受け取る拡張メソッドと新しく追加されたReadOnlySpan<char>オーバーロードの両方よりも優先されます。 このため、 String.Split の新しいオーバーロードは保持されていました。

Note

削除されたメソッドへの呼び出しが確実に削除されるように、.NET 9 Preview 6、.NET 9 Preview 7、.NET 9 RC1、または .NET 9 RC2 に対してビルドされたアセンブリを再構築する必要があります。 これを行わないと、実行時に MissingMethodException が発生する可能性があります。

導入されたバージョン

.NET 9 GA

以前の動作

.NET 9 Preview 6、.NET 9 Preview 7、.NET 9 RC1、.NET 9 RC2 でコンパイルされた次のコード。

private static readonly char[] s_allowedWhitespace = { ' ', '\t', '\u00A0', '\u2000' };

// Only remove the ASCII whitespace.
str = str.Trim(s_allowedWhitespace.AsSpan(0, 2));

.NET 9 Preview 6 より前では、次のコードで "prefixinfix"が生成されました。 .NET 9 Preview 6 から .NET 9 RC2 の場合、代わりに次の "prefixin"が生成されます。

internal static string TrimEnd(this string target, string suffix)
{
    if (target.EndsWith(suffix))
    {
        return target.Substring(0, target.Length - suffix.Length);
    }

    return target;
}

...
return "prefixinfixsuffix".TrimEnd("suffix");

新しい動作

配列のスライスを明示的に使用する次のコードは、呼び出しに適したオーバーロードがないため、コンパイルされなくなります。

private static readonly char[] s_allowedWhitespace = { ' ', '\t', '\u00A0', '\u2000' };

// Only remove the ASCII whitespace.
str = str.Trim(s_allowedWhitespace.AsSpan(0, 2));

拡張メソッドを備えたコード string TrimEnd(this string target, this string suffix) .NET 8 以前のバージョンと同じ動作になりました。 つまり、 "prefixinfix"が生成されます。

破壊的変更の種類

この変更は、バイナリの互換性ソースの互換性に影響する可能性があります。

変更理由

多くのプロジェクトには、再コンパイル後に動作の変更が発生する拡張メソッドがあります。 これらの新しいインスタンス メソッドの悪影響は、その肯定的な利点を上回ると見なされました。

.NET 9 Preview 6、.NET 9 Preview 7、.NET 9 RC1、または .NET 9 RC2 に対してビルドされたすべてのプロジェクトを再コンパイルします。 プロジェクトがエラーなしでコンパイルされる場合、それ以上の作業は必要ありません。 プロジェクトがコンパイルされなくなった場合は、コードを調整します。 可能な置換例の 1 つを次に示します。

-private static ReadOnlySpan<char> s_trimChars = [ ';', ',', '.' ];
+private static readonly char[] s_trimChars = [ ';', ',', '.' ];

...

return input.Trim(s_trimChars);

影響を受ける API

  • System.String.Trim(System.ReadOnlySpan{System.Char})
  • System.String.TrimEnd(System.ReadOnlySpan{System.Char})
  • System.String.TrimStart(System.ReadOnlySpan{System.Char})