Sobrecargas String.Trim*(params ReadOnlySpan<char>) eliminadas
En el ecosistema de .NET, ReadOnlySpan<char>
puede representar:
- Secuencia específica de caracteres, a menudo como un segmento de una instancia mayor System.String .
- Colección de caracteres únicos, a menudo como un segmento de .
char[]
Las versiones anteriores de .NET 9 agregaron params ReadOnlySpan<T>
sobrecargas a grupos de métodos que ya tenían una params T[]
sobrecarga. Aunque esta sobrecarga fue una adición positiva para algunos grupos de métodos, la naturaleza dual de ReadOnlySpan<char>
puede causar confusión para un grupo de métodos que acepta y char[]
un String (en la misma posición) y se tratan de forma diferente. Por ejemplo, public static string [String::]Split(string separator, StringSplitOptions options)
considera la secuencia de caracteres como un separador. Por ejemplo, "[]ne]-[Tw[]".Split("]-[", StringSplitOptions.None)
se divide en new string[] { "[]ne", "Tw[]" };
. Por otro lado, public static [String::]Split(char[] separator, StringSplitOptions options)
considera cada carácter en separator
como un separador distinto, por lo que la división equivalente a la matriz produce new string[] { "", "", "ne", "", "", "Tw", "", "" }
. Por lo tanto, cualquier nueva sobrecarga que acepte un ReadOnlySpan<char>
tiene que decidir si es similar a la cadena o a la matriz. Por lo general, .NET se ajusta al comportamiento similar a la matriz.
Tenga en cuenta las siguientes sobrecargas nuevas String que aceptan un ReadOnlySpan<char>
argumento como se propone en 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);
Además, tenga en cuenta el siguiente método de extensión comúnmente 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;
}
}
En el caso de los entornos de ejecución de .NET existentes, este método de extensión quita la secuencia especificada del final de la cadena. Sin embargo, debido a las reglas de resolución de sobrecarga de C#, "12345!!!!".TrimEnd("!!!")
prefiere la nueva TrimEnd
sobrecarga sobre el método de extensión existente y cambia el resultado de "12345!"
(quitando solo un conjunto completo de tres signos de exclamación) a "12345"
(quitando todas las signos de exclamación del final).
Para resolver este salto, había dos rutas de acceso posibles: introducir un método public string TrimEnd(string trimString)
de instancia que sea un destino aún mejor o quitar el nuevo método. La primera opción conlleva riesgos adicionales, ya que debe decidir si devuelve una instancia de la cadena de destino o todas ellas. Y sin duda hay autores de llamadas con código existente que usa cada enfoque. Por lo tanto, la segunda opción era la opción más adecuada para esta fase del ciclo de lanzamiento.
Los autores de llamadas de String.Trim que pasan caracteres individuales mediante la params
característica, por ejemplo, str.Trim(';', ',', '.')
, no verán una interrupción. El código cambiará automáticamente de llamar string.Trim(params char[])
a string.Trim(params ReadOnlySpan<char>)
. Cuando se vuelve a generar con la versión de disponibilidad general de .NET 9, el compilador volverá automáticamente a la char[]
sobrecarga.
Los autores de llamadas de String.Trim los que pasan explícitamente ( ReadOnlySpan<char>
o un tipo que se puede convertir en ReadOnlySpan<char>
que no también se puede convertir a char[]
) deben cambiar su código para llamar Trim
correctamente después de este cambio.
En cuanto a String.Split, a diferencia String.Trimde , este método ya tiene una sobrecarga que se prefiere sobre un método de extensión que acepta un único parámetro de cadena y la sobrecarga recién agregada ReadOnlySpan<char>
. Por este motivo, se conserva la nueva sobrecarga de String.Split .
Nota:
Debe recompilar cualquier ensamblado compilado en .NET 9 Preview 6, .NET 9 Preview 7, .NET 9 RC1 o .NET 9 RC2 para asegurarse de que se quiten las llamadas al método quitado. Si no lo hace, podría producirse un MissingMethodException error en tiempo de ejecución.
Versión introducida
Disponibilidad general de .NET 9
Comportamiento anterior
El código siguiente compilado en .NET 9 Preview 6, .NET 9 Preview 7, .NET 9 RC1 y .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 de .NET 9 Preview 6, el código siguiente produjo "prefixinfix"
. Para .NET 9 Preview 6 a .NET 9 RC2, en su lugar produjo "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");
Comportamiento nuevo
El código siguiente que usa explícitamente un segmento de una matriz ya no se compila, ya que no hay ninguna sobrecarga adecuada para que llame a:
private static readonly char[] s_allowedWhitespace = { ' ', '\t', '\u00A0', '\u2000' };
// Only remove the ASCII whitespace.
str = str.Trim(s_allowedWhitespace.AsSpan(0, 2));
El código que incluye un método string TrimEnd(this string target, this string suffix)
de extensión ahora tiene el mismo comportamiento que tenía en .NET 8 y versiones anteriores. Es decir, produce "prefixinfix"
.
Tipo de cambio importante
Este cambio puede afectar a la compatibilidad binaria y a la compatibilidad de orígenes.
Motivo del cambio
Muchos proyectos tienen métodos de extensión que experimentan cambios de comportamiento después de volver a compilar. El impacto negativo de estos nuevos métodos de instancia se considera que supera su beneficio positivo.
Acción recomendada
Vuelva a compilar los proyectos compilados con .NET 9 Preview 6, .NET 9 Preview 7, .NET 9 RC1 o .NET 9 RC2. Si el proyecto se compila sin errores, no se requiere ningún trabajo adicional. Si el proyecto ya no se compila, ajuste el código. Aquí se muestra un ejemplo de sustitución posible:
-private static ReadOnlySpan<char> s_trimChars = [ ';', ',', '.' ];
+private static readonly char[] s_trimChars = [ ';', ',', '.' ];
...
return input.Trim(s_trimChars);
API afectadas
System.String.Trim(System.ReadOnlySpan{System.Char})
System.String.TrimEnd(System.ReadOnlySpan{System.Char})
System.String.TrimStart(System.ReadOnlySpan{System.Char})