Partilhar via


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

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

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

Versões anteriores do .NET 9 adicionavam 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étodo, a natureza dupla de pode causar confusão para um grupo de ReadOnlySpan<char> método 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 ele é string-like ou array-like. De um modo geral, o .NET está em conformidade com o comportamento semelhante à 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 tempos de execução .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 sobre o 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 quebra, 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 acarreta riscos adicionais, pois precisa decidir se retorna uma instância da cadeia de caracteres de destino ou todas elas. E há, sem dúvida, 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 de String.Trim quem passar caracteres individuais usando o params recurso, por exemplo, str.Trim(';', ',', '.')não verão uma pausa. Seu código terá mudado automaticamente de chamada string.Trim(params char[]) para string.Trim(params ReadOnlySpan<char>). Quando você reconstruir em relação à versão GA do .NET 9, o compilador voltará automaticamente para a char[] sobrecarga.

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

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

Nota

Você deve reconstruir 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. Se não o fizer, poderá ocorrer um MissingMethodException tempo de execução.

Versão introduzida

.NET 9 GA

Comportamento anterior

O código a seguir compilado no .NET 9 Preview 6, .NET 9 Preview 7, .NET 9 RC1 e .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"o . Para o .NET 9 Preview 6 a .NET 9 RC2, em vez disso, gerou "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 que ele seja chamado:

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 versões anteriores. Ou seja, rende "prefixinfix".

Tipo de mudança de rutura

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

Razão para a alteração

Muitos projetos têm métodos de extensão que experimentam mudanças comportamentais após a recompilação. Considerou-se que o impacto negativo destes novos métodos de instância superava os seus benefícios positivos.

Recompile todos os projetos que foram criados em relação ao .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})