Compartilhar via


Sobrecargas de String.Trim*(params ReadOnlySpan<char>) removidas

No ecossistema .NET, ReadOnlySpan<char> pode representar:

  • Uma sequência específica de caracteres, geralmente como uma fatia de uma instância maior System.String .
  • Uma coleção de caracteres únicos, geralmente como uma fatia de um char[]arquivo .

Versões anteriores do .NET 9 adicionaram params ReadOnlySpan<T> sobrecargas a grupos de métodos que já tinham uma params T[] sobrecarga. Embora essa sobrecarga tenha sido uma adição positiva para alguns grupos de métodos, a natureza dupla de pode causar confusão para um grupo de ReadOnlySpan<char> métodos que aceita a char[] e a String (na mesma posição) e eles são tratados de forma diferente. Como exemplo, public static string [String::]Split(string separator, StringSplitOptions options) considera a sequência de caracteres como um separador. Por exemplo, "[]ne]-[Tw[]".Split("]-[", StringSplitOptions.None) divide-se em new string[] { "[]ne", "Tw[]" };. Por outro lado, public static [String::]Split(char[] separator, StringSplitOptions options) considera cada caractere como separator um separador distinto, de modo que a divisão equivalente à matriz produz new string[] { "", "", "ne", "", "", "Tw", "", "" }. Portanto, qualquer nova sobrecarga que aceite um ReadOnlySpan<char> tem que decidir se é semelhante a uma cadeia de caracteres ou semelhante a uma matriz. De modo geral, o .NET está em conformidade com o comportamento semelhante a uma matriz.

Considere as seguintes novas String sobrecargas que aceitam um ReadOnlySpan<char> argumento conforme proposto em dotnet/runtime#77873:

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);

Além disso, considere o seguinte método de extensão comumente definido:

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;
    }
}

Para runtimes .NET existentes, esse método de extensão remove a sequência especificada do final da cadeia de caracteres. No entanto, devido às regras de resolução de sobrecarga do C#, "12345!!!!".TrimEnd("!!!") preferirá a nova TrimEnd sobrecarga ao método de extensão existente e alterará o resultado de "12345!" (removendo apenas um conjunto completo de três pontos de exclamação) para "12345" (removendo todos os pontos de exclamação do final).

Para resolver essa interrupção, havia dois caminhos possíveis: introduzir um método public string TrimEnd(string trimString) de instância que seja um destino ainda melhor ou remover o novo método. A primeira opção traz risco adicional, pois precisa decidir se retorna uma instância da cadeia de caracteres de destino ou todas elas. E, sem dúvida, existem chamadores com código existente que usa cada abordagem. Portanto, a segunda opção foi a escolha mais adequada para esta etapa do ciclo de lançamento.

Os chamadores que String.Trim passam caracteres individuais usando o params recurso, por exemplo, str.Trim(';', ',', '.'), não verão uma interrupção. Seu código terá mudado automaticamente de chamada string.Trim(params char[]) para string.Trim(params ReadOnlySpan<char>). Quando você recompilar na versão GA do .NET 9, o compilador voltará automaticamente para a char[] sobrecarga.

Os chamadores que String.Trim passam explicitamente um ReadOnlySpan<char> (ou um tipo que é conversível para ReadOnlySpan<char> que também não seja conversível para char[]) devem alterar seu código para chamar Trim com êxito após essa alteração.

Quanto a String.Split, ao contrário String.Trimde , esse método já tem uma sobrecarga que é preferível a um método de extensão que aceita um único parâmetro de cadeia de caracteres e a sobrecarga recém-adicionada ReadOnlySpan<char> . Por esse motivo, a nova sobrecarga de String.Split foi preservada.

Observação

Você deve recompilar qualquer assembly criado no .NET 9 Preview 6, .NET 9 Preview 7, .NET 9 RC1 ou .NET 9 RC2 para garantir que todas as chamadas para o método removido sejam removidas. Não fazer isso pode resultar em um tempo de MissingMethodException execução.

Versão introduzida

.NET 9 GA

Comportamento anterior

O código a seguir compilado no .NET 9 Preview 6, no .NET 9 Preview 7, no .NET 9 RC1 e no .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));

Antes do .NET 9 Preview 6, o código a seguir produzia "prefixinfix"arquivos . Para o .NET 9 Preview 6 até o .NET 9 RC2, ele produziu "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");

Novo comportamento

O código a seguir que usa explicitamente uma fatia de uma matriz não é mais compilado, pois não há sobrecarga adequada para ele chamar:

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

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

O código que apresenta um método string TrimEnd(this string target, this string suffix) de extensão agora tem o mesmo comportamento que tinha no .NET 8 e em versões anteriores. Ou seja, rende "prefixinfix".

Tipo de alteração interruptiva

Essa alteração pode afetar a compatibilidade binária e a compatibilidade de origem.

Motivo da alteração

Muitos projetos têm métodos de extensão que sofrem mudanças comportamentais após a recompilação. O impacto negativo desses novos métodos de instância foi considerado superior ao seu benefício positivo.

Recompile todos os projetos que foram criados no .NET 9 Preview 6, .NET 9 Preview 7, .NET 9 RC1 ou .NET 9 RC2. Se o projeto for compilado sem erros, nenhum trabalho adicional será necessário. Se o projeto não for mais compilado, ajuste seu código. Um possível exemplo de substituição é mostrado aqui:

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

...

return input.Trim(s_trimChars);

APIs afetadas

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