Runtime: Rompiendo el cambio con string.IndexOf (string) de .NET Core 3.0 -> .NET 5.0

Creado en 22 oct. 2020  ·  76Comentarios  ·  Fuente: dotnet/runtime

Descripción

Estoy ampliando un paquete para admitir .NET 5.0 y encontré un cambio importante. Dada la aplicación de consola:

using System;

namespace ConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            var actual = "Detail of supported commands\n============\n## Documentation produced for DelegateDecompiler, version 0.28.0 on Thursday, 22 October 2020 16:03\n\r\nThis file documents what linq commands **DelegateDecompiler** supports when\r\nworking with [Entity Framework Core](https://docs.microsoft.com/en-us/ef/core/) (EF).\r\nEF has one of the best implementations for converting Linq `IQueryable<>` commands into database\r\naccess commands, in EF's case T-SQL. Therefore it is a good candidate for using in our tests.\r\n\r\nThis documentation was produced by compaired direct EF Linq queries against the same query implemented\r\nas a DelegateDecompiler's `Computed` properties. This produces a Supported/Not Supported flag\r\non each command type tested. Tests are groups and ordered to try and make finding things\r\neasier.\r\n\r\nSo, if you want to use DelegateDecompiler and are not sure whether the linq command\r\nyou want to use will work then clone this project and write your own tests.\r\n(See [How to add a test](HowToAddMoreTests.md) documentation on how to do this). \r\nIf there is a problem then please fork the repository and add your own tests. \r\nThat will make it much easier to diagnose your issue.\r\n\r\n*Note: The test suite has only recently been set up and has only a handful of tests at the moment.\r\nMore will appear as we move forward.*\r\n\r\n\r\n### Group: Unit Test Group\n#### [My Unit Test1](../TestGroup01UnitTestGroup/Test01MyUnitTest1):\n- Supported\n  * Good1 (line 1)\n  * Good2 (line 2)\n\r\n#### [My Unit Test2](../TestGroup01UnitTestGroup/Test01MyUnitTest2):\n- Supported\n  * Good1 (line 1)\n  * Good2 (line 2)\n\r\n\r\n\nThe End\n";

            var expected = "\n#### [My Unit Test2](";

            Console.WriteLine($"actual.Contains(expected): {actual.Contains(expected)}");
            Console.WriteLine($"actual.IndexOf(expected): {actual.IndexOf(expected)}");
        }
    }
}

Obtengo resultados diferentes basados ​​en el tiempo de ejecución de .NET Core 3.0 -> .NET 5.0:

.NET Core 3.0:

actual.Contains(expected): True
actual.IndexOf(expected): 1475

.NET 5.0:

actual.Contains(expected): True
actual.IndexOf(expected): -1

Configuración

Windows 10 Pro compilación 19041 x64
.NET Core 3.1.9
.NET 5.0.0-rc.2.20475.5

¿Regresión?

Sí, funcionó a través de .NET Core 3.1.9

area-System.Globalization question

Comentario más útil

@tarekgh , estoy de acuerdo en que los diferentes resultados entre Contains y IndexOf no son el problema per se.

El problema es claramente IndexOf que no puede encontrar una cadena solo ASCII dentro de otra cadena solo ASCII (¡no estoy seguro de que alguna vez haya habido un comportamiento dependiente de la configuración regional impuesto en cadenas solo ASCII!).

Esto no es algo que esperaría de ningún cambio relacionado con la configuración regional / NLS / ICU; de hecho, no pude pensar en ningún otro lenguaje de programación / tiempo de ejecución que se comporte así.

Aquí hay un caso de prueba simplificado, roto (quiero decir, dándome un resultado totalmente inesperado) en .NET 5 RC 2:

var actual = "\n\r\nTest";
var expected = "\nTest";

Console.WriteLine($"actual.IndexOf(expected): {actual.IndexOf(expected)}"); // => -1

¿Debería realmente funcionar así? Además, ¿por qué? ¿Qué intenta hacer en realidad?

¿Fue realmente un cambio planeado?

Sí, el cambio a la UCI es un cambio intencional por diferentes razones.

Lo siento, pero no creo que este sea un cambio planeado, así que me gustaría enfatizar: no podía imaginar a nadie _planificando_ tal cambio. Al igual que, la gente del equipo de .NET se sentaron juntos y discutieron:

¿La cadena "\ n \ r \ nTest" contiene "\ nTest" con ICU habilitado? ¡No, claramente no lo hace!

¿Y nadie se quejó? ¡De ninguna manera!

Esto no parece un cambio planeado o esperado, sino que parece un error muy grave, un gran bloqueador de compatibilidad. Por eso, las aplicaciones .NET nuevas y portadas no funcionarán correctamente en el nuevo tiempo de ejecución, ¡porque no podrán encontrar subcadenas dentro de la cadena!

De todos modos, ¿por qué a la UCI le preocupan los extremos de las líneas? ¿Algunas configuraciones regionales tienen sus propios finales de línea específicos de la configuración regional?

PD: Sí, se podría argumentar que siempre se debería llamar a alguna variante de IndexOf independiente de la cultura, como con la bandera ordinal. Pero, si ha decidido romperlo _ tan difícilmente en .NET 5, ¿no podría simplemente hacerlo para usar el ordinal predeterminado sano? Creo que rompería menos aplicaciones que el cambio actual que estamos viendo en .NET 5 RC 2.

Además, creo que todos entendemos que, a pesar de que IndexOf siempre se comporta de una manera específica de la cultura, hay _ toneladas_ de código en la naturaleza que usan IndexOf sin las banderas ordinales, y ese código _ acostumbrado a trabajar_ (en algunos / la mayoría de los casos, al menos). Y dejará de funcionar después de la actualización de .NET 5.

Todos 76 comentarios

@tarekgh

Etiquetado de suscriptores a esta área: @tarekgh , @safern , @krwq
Consulte la información en area-owners.md si desea suscribirse.

Esto es por diseño, ya que en .NET 5.0 hemos cambiado usando ICU en lugar de NLS. Puede consultar https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization-icu para obtener más detalles.

Tiene la opción de usar el conmutador de configuración System.Globalization.UseNls para volver al comportamiento anterior, pero no recomendamos hacerlo ya que ICU es más correcto y avanzar con ICU dará consistencia en todos los sistemas operativos.

Olvidé decir, si desea que IndexOf comporte como Contains , debe usar las comparaciones ordinales en ese momento.

C# actual.IndexOf(expected, StringComparison.Ordinal)

Tiene la opción de usar el conmutador de configuración System.Globalization.UseNls para volver al comportamiento anterior, pero no recomendamos hacerlo ya que ICU es más correcto y avanzar con ICU dará consistencia en todos los sistemas operativos.

Sí, si ejecuta este código en Unix apuntando a netcoreapp3.1 verá este mismo comportamiento:

 santifdezm  DESKTOP-1J7TFMI  ~  experimental  indexof  $   /home/santifdezm/experimental/indexof/bin/Debug/netcoreapp3.1/linux-x64/publish/indexof
actual.Contains(expected): True
actual.IndexOf(expected): -1

y como @tarekgh con Ordinal comparación devuelve el resultado esperado.

 santifdezm  DESKTOP-1J7TFMI  ~  experimental  indexof  $  /home/santifdezm/experimental/indexof/bin/Debug/net5.0/linux-x64/publish/indexof
actual.Contains(expected): True
actual.IndexOf(expected): 1475

Creo que esto está fallando porque la combinación de \r\n y \n en la cadena de origen. Si reemplazo todas las instancias de \r\n con \n , funciona. Lo mismo es cierto si hago todo \r\n . Es solo la combinación la que da como resultado resultados diferentes de la comparación de la UCI.

El problema, como se informó en Twitter, fue que dentro del tiempo de ejecución de 5.0, las llamadas repetidas a string.IndexOf con las mismas entradas daban resultados diferentes en cada llamada.

https://twitter.com/jbogard/status/1319381273585061890?s=21

Editar: Arriba hubo un malentendido.

@GrabYourPitchforks ¿podemos actualizar el título del problema entonces? Como esto es técnico, un cambio importante, pero esto sucede en Windows y Unix ... ¿verdad?

Hice ping a Jimmy fuera de línea para una aclaración. Es posible que haya entendido mal su informe de problemas original. Los foros de 280 caracteres no siempre son eficientes para comunicar errores con claridad. ;)

Solo para aclarar, Contains API está realizando una operación ordinal. IndexOf sin marcas de comparación de cadenas es una operación lingüística y no ordinal. Si desea comparar el comportamiento de Contiene con IndexOf, debe usar IndexOf(expected, StringComparison.Ordinal) .
Si necesita obtener más información sobre la diferencia, https://docs.microsoft.com/en-us/dotnet/csharp/how-to/compare-strings es un enlace útil.

Recibí una aclaración en Twitter. La aplicación no llama a IndexOf en un bucle. Este es solo un informe estándar de diferencias de comportamiento 3.0 frente a 5.0.

@GrabYourPitchforks, ¿ podría compartir el enlace https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization-icu en sus respuestas de Twitter y mencionar que tenemos un interruptor de configuración para volver al comportamiento anterior?

Recibí una aclaración en Twitter. La aplicación no llama a IndexOf en un bucle. Este es solo un informe estándar de diferencias de comportamiento 3.0 frente a 5.0.

Gracias, @GrabYourPitchforks ... basándome en esto, cerrándolo como por diseño.

Para agregar más aquí, si desea obtener el comportamiento anterior sin volver a NLS, puede hacer

`` C #
CultureInfo.CurrentCulture.CompareInfo.IndexOf (real, esperado, CompareOptions.IgnoreSymbols)


or 

```C#
    actual.IndexOf(expected, StringComparison.Ordinal)

en vez de

C# actual.IndexOf(expected)

y debería obtener el comportamiento deseado.

No puedo ver nada sobre \r\n vs \n en la documentación relacionada con la UCI vinculada (https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/ globalización-icu).

¿Fue realmente un cambio planeado?

@ForNeVeR será difícil enumerar todas las diferencias entre ICU y NLS. el documento habla del cambio principal de cambiar a UCI. Como señalé anteriormente, no es correcto comparar los resultados de Contains con IndexOf sin los parámetros StringComparison. He enumerado anteriormente un par de formas en que puede obtener el comportamiento anterior si lo desea. Por el informe de este problema, me parece que el uso de IndexOf debería usar la opción Ordinal y es incorrecto usar las comparaciones lingüísticas en tal caso. El uso de la comparación lingüística en tal caso puede depender de la cultura actual, lo que puede dar diferentes resultados en diferentes entornos.

¿Fue realmente un cambio planeado?

Sí, el cambio a la UCI es un cambio intencional por diferentes razones. Windows promociona actualmente el uso de ICU sobre NLS. La UCI es el futuro de todos modos. Además, ICU brindará la oportunidad de tener comportamientos consistentes en Windows / Linux / OSX o cualquier plataforma compatible. El uso de ICU dará la oportunidad a las aplicaciones de personalizar el comportamiento de globalización si así lo desean.

Como se indica en el documento, aún tiene la opción de volver al comportamiento anterior si lo desea.

Ay, el documento al que se hace referencia dice que el comportamiento de ICU / NLS en Windows podría cambiar silenciosamente en función de la disponibilidad de icu.dll en el entorno real. Esto podría ser una gran sorpresa para las aplicaciones independientes publicadas. Espero que .NET envíe ICU para solucionar este problema si se decide el cambio, y dado que ICU no está disponible en todos los entornos de destino. Esta dependencia de tiempo de ejecución opcional hace que las cosas sean aún más divertidas.

Espero que .NET envíe ICU para solucionar este problema si se decide el cambio, y dado que ICU no está disponible en todos los entornos de destino. Esta dependencia de tiempo de ejecución opcional hace que las cosas sean aún más divertidas.

La UCI ahora se publica como paquete NuGet. Las aplicaciones pueden usar dichos paquetes para que la aplicación autónoma se asegure de tener UCI. mire la sección de aplicación local en el documento . En resumen, la aplicación tiene un control total sobre el comportamiento que desea obtener.

@tarekgh , estoy de acuerdo en que los diferentes resultados entre Contains y IndexOf no son el problema per se.

El problema es claramente IndexOf que no puede encontrar una cadena solo ASCII dentro de otra cadena solo ASCII (¡no estoy seguro de que alguna vez haya habido un comportamiento dependiente de la configuración regional impuesto en cadenas solo ASCII!).

Esto no es algo que esperaría de ningún cambio relacionado con la configuración regional / NLS / ICU; de hecho, no pude pensar en ningún otro lenguaje de programación / tiempo de ejecución que se comporte así.

Aquí hay un caso de prueba simplificado, roto (quiero decir, dándome un resultado totalmente inesperado) en .NET 5 RC 2:

var actual = "\n\r\nTest";
var expected = "\nTest";

Console.WriteLine($"actual.IndexOf(expected): {actual.IndexOf(expected)}"); // => -1

¿Debería realmente funcionar así? Además, ¿por qué? ¿Qué intenta hacer en realidad?

¿Fue realmente un cambio planeado?

Sí, el cambio a la UCI es un cambio intencional por diferentes razones.

Lo siento, pero no creo que este sea un cambio planeado, así que me gustaría enfatizar: no podía imaginar a nadie _planificando_ tal cambio. Al igual que, la gente del equipo de .NET se sentaron juntos y discutieron:

¿La cadena "\ n \ r \ nTest" contiene "\ nTest" con ICU habilitado? ¡No, claramente no lo hace!

¿Y nadie se quejó? ¡De ninguna manera!

Esto no parece un cambio planeado o esperado, sino que parece un error muy grave, un gran bloqueador de compatibilidad. Por eso, las aplicaciones .NET nuevas y portadas no funcionarán correctamente en el nuevo tiempo de ejecución, ¡porque no podrán encontrar subcadenas dentro de la cadena!

De todos modos, ¿por qué a la UCI le preocupan los extremos de las líneas? ¿Algunas configuraciones regionales tienen sus propios finales de línea específicos de la configuración regional?

PD: Sí, se podría argumentar que siempre se debería llamar a alguna variante de IndexOf independiente de la cultura, como con la bandera ordinal. Pero, si ha decidido romperlo _ tan difícilmente en .NET 5, ¿no podría simplemente hacerlo para usar el ordinal predeterminado sano? Creo que rompería menos aplicaciones que el cambio actual que estamos viendo en .NET 5 RC 2.

Además, creo que todos entendemos que, a pesar de que IndexOf siempre se comporta de una manera específica de la cultura, hay _ toneladas_ de código en la naturaleza que usan IndexOf sin las banderas ordinales, y ese código _ acostumbrado a trabajar_ (en algunos / la mayoría de los casos, al menos). Y dejará de funcionar después de la actualización de .NET 5.

El problema es claramente IndexOf, que no puede encontrar una cadena solo ASCII dentro de otra cadena solo ASCII (¡no estoy seguro de que alguna vez haya habido un comportamiento dependiente de la configuración regional impuesto en cadenas solo ASCII!).

No es cierto que ASCII sea independiente de la configuración regional. mire el enlace http://userguide.icu-project.org/collation/concepts como un ejemplo de cómo el comportamiento de los caracteres ASCII puede diferir para diferentes culturas.

For example, in the traditional Spanish sorting order, "ch" is considered a single letter. All words that begin with "ch" sort after all other words beginning with "c", but before words starting with "d".
Other examples of contractions are "ch" in Czech, which sorts after "h", and "lj" and "nj" in Croatian and Latin Serbian, which sort after "l" and "n" respectively.

Además, quiero aclarar que ICU elige sus datos y comportamiento del estándar Unicode, que está bien pensado por muchos expertos. @GrabYourPitchforks publicará más detalles sobre el caso \r\n\ que estamos hablando aquí. pero mientras tanto, puede familiarizarse con el documento https://unicode.org/reports/tr29/ especialmente en las secciones que mencionan lo siguiente:

Do not break between a CR and LF. Otherwise, break before and after controls.
--
GB3 | CR | × | LF
GB4 | (Control \| CR \| LF) | ÷ |  
GB5 |   | ÷ | (Control \| CR \| LF)

De todos modos, ¿por qué a la UCI le preocupan los extremos de las líneas? ¿Algunas configuraciones regionales tienen sus propios finales de línea específicos de la configuración regional?

Esto se aborda en el último párrafo.

Lo siento, pero no creo que este sea un cambio planeado, así que me gustaría enfatizar: no podía imaginarme a nadie planeando tal cambio. Al igual que, la gente del equipo de .NET se sentaron juntos y discutieron:
Esto no parece un cambio planeado o esperado, sino que parece un error muy grave, un gran bloqueador de compatibilidad. Por eso, las aplicaciones .NET nuevas y portadas no funcionarán correctamente en el nuevo tiempo de ejecución, ¡porque no podrán encontrar subcadenas dentro de la cadena!

Esto está bien planeado, trabajado y pensado profundamente en él. puede ver el problema https://github.com/dotnet/runtime/issues/826 que publicamos hace mucho tiempo y lo compartimos públicamente.
También quiero enfatizar que el comportamiento de la globalización puede cambiar en cualquier momento no solo para .NET sino también para los sistemas operativos y otras plataformas. Esta es también la razón por la que admitimos la función local de la aplicación ICU para permitir que las aplicaciones usen una versión específica de ICU para garantizar que el comportamiento que usan no va a cambiar. Otra cosa, el propio Windows está en proceso de promover el uso de UCI y algún día el comportamiento de la UCI será lo que la mayoría de los usuarios usarán.

como con la bandera ordinal. Pero, si ha decidido romperlo con tanta fuerza en .NET 5, ¿no podría hacerlo para usar el ordinal predeterminado sano? Creo que rompería menos aplicaciones que el cambio actual que estamos viendo en .NET 5 RC 2.

En realidad, hemos intentado antes hacer que el comportamiento Ordinal sea el predeterminado antes durante las versiones de Silverlight y eso causó muchos más problemas de los que se informa aquí. Estamos buscando más formas de ayudar a los desarrolladores a ser conscientes al llamar a algo como IndexOf y proporcionar intencionalmente indicadores StringComparison para expresar la intención. También agradecemos cualquier idea que se le ocurra.

Además, creo que todos entendemos que, a pesar de que IndexOf siempre se comporta de una manera específica de la cultura, hay toneladas de código en la naturaleza que usan IndexOf sin las banderas ordinales, y ese código solía funcionar (en algunos / la mayoría de los casos, al menos). Y dejará de funcionar después de la actualización de .NET 5.

Es por eso que proporcionamos un interruptor de configuración para volver al comportamiento anterior si usted también lo desea. mira System.Globalization.UseNls

@tarekgh , ¡gracias por la explicación detallada!

Por ahora, creo que es mejor para mí esperar los detalles sobre este comportamiento \r\n . No me queda claro cómo IndexOf usa "Reglas de límites de clúster de Grapheme" (y si debería hacerlo) al realizar esta búsqueda en particular).

Además, incluso si la especificación Unicode es relevante aquí (¡que muy bien puede ser!), Al leer https://unicode.org/reports/tr29/ , no estoy seguro de que prohíba hacer coincidir los últimos \n . Mientras leo la especificación, dice CR | × | LF , donde × significa "Sin límite (no permitir ruptura aquí)". Entonces, al romper una secuencia \r\n\n , solo prohíbe colocar el "salto" entre el primer y el segundo caracteres, pero debería estar bien colocar el "salto" antes del tercero, ¿no? Entonces, leo \r\n\n como dos "grupos de grafemas" separados, e incluso si IndexOf solo tiene que coincidir con los grupos de grafemas completos y nunca tocar partes de los grupos, debería encontrar la subcadena \nTest dentro de la cadena \n\r\nTest .

Además, ¿está diciendo que otros tiempos de ejecución / lenguajes de programación que dependen de la especificación ICU y / o Unicode deberían comportarse de la misma manera con este ejemplo en particular?

Estamos buscando más formas de ayudar a los desarrolladores a ser conscientes al llamar a algo como IndexOf y proporcionar intencionalmente indicadores StringComparison para expresar la intención. También agradecemos cualquier idea que se le ocurra.

_ (Un descargo de responsabilidad necesario: trabajo para JetBrains en varios proyectos, incluido ReSharper.) _

Inicialmente no quería traer este punto aquí para que no sonara como un anuncio, pero creo que esto es muy relevante, así que tendré que hacerlo. ReSharper de forma predeterminada mostrará la siguiente advertencia para el código de usuario que llama IndexOf :
image

_ (tenga en cuenta que no estaba al tanto de este diagnóstico de ReSharper en particular antes de participar en este hilo, por lo que no estoy participando aquí solo para presentar este argumento) _

Por lo tanto, creo que sería una muy buena idea mostrar dicha notificación de forma predeterminada en todas las demás herramientas también, o tal vez incluso desaprobar totalmente este método falso con tal aviso.

@Para nunca

Además, incluso si la especificación Unicode es relevante aquí (¡que muy bien puede serlo!), Al leer unicode.org/reports/tr29, no estoy seguro de que prohíba coincidir con esa última \ n. Mientras leo las especificaciones, dice CR | × | LF, donde × significa "Sin límite (no permitir que se rompa aquí)". Entonces, al romper una secuencia \ r \ n \ n, solo prohíbe colocar el "salto" entre el primer y el segundo caracteres, pero debería estar bien colocar el "salto" antes del tercero, ¿no? Entonces, leo \ r \ n \ n como dos "grupos de grafemas" separados

Eso es correcto. \r\n\n serán 2 grupos como \r\n y \n .

e, incluso si IndexOf solo tiene que coincidir con los grupos de grafemas completos y nunca tocar partes de los grupos, debería encontrar la subcadena \ nTest dentro de la cadena \ n \ r \ nTest.

Eso es incorrecto. \n\r\nTest se dividirá en partes. \n , \r\n y Test . es obvio que \nTest no puede formar parte de esta cadena. piense en reemplazar el clúster \r\n con algún símbolo X . ahora la cadena de origen será \nXTest que no contiene \nTest .

Además, ¿está diciendo que otros tiempos de ejecución / lenguajes de programación que dependen de la especificación ICU y / o Unicode deberían comportarse de la misma manera con este ejemplo en particular?

Si usa el nivel de fuerza de clasificación predeterminado, la respuesta es sí. La UCI puede permitir cambiar el nivel de fuerza que puede afectar el resultado. Por ejemplo, como mencioné anteriormente, hacer algo como:

CultureInfo.CurrentCulture.CompareInfo.IndexOf(actual, expected, CompareOptions.IgnoreSymbols)

cambiará el nivel de fuerza y ​​hará que la operación ignore los símbolos (lo que cambiará el comportamiento de \n y \r ya que se ignorará en ese momento)

Además, escribí una aplicación C nativa de ICU pura y ejecuté el mismo caso:

void SearchString(const char *target, int32_t targetLength, const char *source, int32_t sourceLength)
{
    static UChar usource[100];
    static UChar utarget[100];

    u_charsToUChars(source, usource, sourceLength);
    u_charsToUChars(target, utarget, targetLength);

    UErrorCode status = U_ZERO_ERROR;
    UStringSearch* pSearcher = usearch_open(utarget, targetLength, usource, sourceLength, "en_US", nullptr, &status);
    if (!U_SUCCESS(status))
    {
        printf("usearch_open failed with %d\n", status);
        return;
    }

    int32_t index = usearch_next(pSearcher, &status);
    if (!U_SUCCESS(status))
    {
        printf("usearch_next failed with %d\n", status);
        return;
    }

    printf("search result = %d\n", index);
    usearch_close(pSearcher);
}


int main()
{
    SearchString("\nT", 2, "\r\nT", 3);
    SearchString("\nT", 2, "\n\nT", 3);
}

esta aplicación generará los resultados:

search result = -1
search result = 1

que es idéntico al comportamiento que está viendo con .NET.

Por ahora, creo que es mejor para mí esperar los detalles sobre este comportamiento \ r \ n en particular. No me queda claro cómo IndexOf usa "Reglas de límites de clúster de Grapheme" (y si debería hacerlo) al realizar esta búsqueda en particular).

Seguro que la agrupación en clústeres está afectando la operación de clasificación. Si mira http://unicode.org/reports/tr29/tr29-7.html , claramente dice lo siguiente:

Grapheme clusters include, but are not limited to, combining character sequences such as (g + °), digraphs such as Slovak “ch”, and sequences with letter modifiers such as kw. límites del clúster de grafemas son importantes para la intercalación , regular-expressions, and counting “character” positions within text. Word boundaries, line boundaries and sentence boundaries do not occur within a grapheme cluster. In this section, the Unicode Standard provides a determination of where the default grapheme boundaries fall in a string of characters. This algorithm can be tailored for specific locales or other customizations, which is what is done in providing contracting characters in collation tailoring tables.

No estoy seguro de si habrá más detalles, pero dejaré que @GrabYourPitchforks comente si tiene más que agregar aquí.

Por lo tanto, creo que sería una muy buena idea mostrar dicha notificación de forma predeterminada en todas las demás herramientas también, o tal vez incluso desaprobar totalmente este método falso con tal aviso.

¡Gracias!
Esta es la misma dirección en la que también estamos pensando.

Por mi propio bien, comparé las diversas sobrecargas entre versiones:

| Método netcoreapp3.1 | net5.0 |
| ------------------------------------------------- --------------- | ----------------- | ---------- |
| actual.Contains(expected) | Verdadero | Verdadero |
| actual.IndexOf(expected) | 1475 | -1 |
| actual.Contains(expected, StringComparison.CurrentCulture) | Verdadero | Falso
| actual.IndexOf(expected, StringComparison.CurrentCulture) | 1475 | -1 |
| actual.Contains(expected, StringComparison.Ordinal) | Verdadero | Verdadero |
| actual.IndexOf(expected, StringComparison.Ordinal) | 1475 | 1475 |
| actual.Contains(expected, StringComparison.InvariantCulture) | Verdadero | Falso
| actual.IndexOf(expected, StringComparison.InvariantCulture) | 1475 | -1 |

Incluya un analizador para esto.

Este parece ser uno de esos cambios que, aunque son buenos a largo plazo, crean una gran cantidad de abandono una vez que se inicia .NET 5. Entonces, si el comportamiento de estos métodos difiere entre .NET 5 y .NET Core 3.1, ¿qué sucederá cuando un .NET 5 llame a un objeto definido dentro de una biblioteca .NET Standard 2.0 que manipula un string pasado a él? desde el sitio de llamadas .NET? ¿Se acostumbra el antiguo comportamiento o el nuevo comportamiento?

@Aaronontheweb nuevo comportamiento. Vi esto inicialmente a partir de una afirmación en NUnit3, que apunta a netstandard2.0 . Después de actualizar, mi prueba comenzó a fallar cuando solo cambié el marco de destino.

Eso no es genial: no puedo controlar lo que hacen las bibliotecas más antiguas a las que hago referencia si quiero actualizar.

¿Cuántas aplicaciones no detectarán eso en las pruebas unitarias y las pondrán en producción?
¿El equipo de .NET consideró el dolor, los problemas y el costo que esto podría causar?

Eso no es genial: no puedo controlar lo que hacen las bibliotecas más antiguas a las que hago referencia si quiero actualizar.

bonita trampa!
feliz perdiendo el tiempo cazando bichos raros 🎉
pero ¿por qué InvariantGlobalization no ayuda con este problema?

Incluya un analizador para esto.

Si. Y no olvide el soporte de F #.

Cuántas aplicaciones no detectarán eso en las pruebas unitarias

Cero, dado que las pruebas unitarias no están destinadas a probar bibliotecas / marcos externos como el propio .NET BCL.

Mi opinión es que debería haber un Atributo de nivel de ensamblaje que pueda controlar el modo de este cambio de comportamiento. Al menos entonces, puede optar por participar o no participar en un nivel por ensamblaje. Esto significa que el problema de .NET Standard también desaparece.

Eso no es genial: no puedo controlar lo que hacen las bibliotecas más antiguas a las que hago referencia si quiero actualizar.

No veo por qué no puedes controlarlo. Todas las comparaciones de cadenas utilizan ICU o NLS. Puede optar por no participar en ICU utilizando el conmutador de compatibilidad si lo desea, y todas sus bibliotecas volverán al comportamiento anterior.

No puede confiar en que los datos de globalización se mantengan estables en el tiempo. El equipo de Windows dice que no tienen miedo de destruir a las personas que dependen de datos estables de globalización. Las funciones de globalización deberían ser una caja negra; No creo que tenga sentido que las bibliotecas (particularmente las que tienen como objetivo .NET Standard) digan que dependen de detalles de implementación como este.

Estoy seguro de que muchas personas se han quejado de que las funciones de globalización de .NET arrojan resultados diferentes en Windows frente a Linux (y tal vez incluso más personas ni siquiera lo han notado). Es mejor unificar el comportamiento entre Windows y otras plataformas, y cualquier código correcto no debería depender de que los datos de globalización sean inmutables, independientemente.

¿Consideraría hacer un cambio importante para que también StringComparison.Ordinal la estrategia de comparación predeterminada? Dado que la globalización es tan inestable, tiene sentido que al menos la implementación predeterminada utilice un algoritmo estable. Estoy dispuesto a apostar que el 99.9% de las personas que usan string.Equals(...) o string.Contains(...) etc. sin pasar StringComparison no lo hacen con la intención explícita de manejar peculiaridades extrañas relacionadas con locales.

Editar: supongo que mi pregunta ya ha sido respondida:

En realidad, hemos intentado antes hacer que el comportamiento Ordinal sea el predeterminado antes durante las versiones de Silverlight y eso causó muchos más problemas de los que se informa aquí. Estamos buscando más formas de ayudar a los desarrolladores a ser conscientes al llamar a algo como IndexOf y proporcionar intencionalmente indicadores StringComparison para expresar la intención. También agradecemos cualquier idea que se le ocurra.

Puede optar por no participar en ICU utilizando el conmutador de compatibilidad si lo desea, y todas sus bibliotecas volverán al comportamiento anterior.

Me gano la vida haciendo bibliotecas, no aplicaciones. Preferiría tener una forma en tiempo de compilación de manejar esto.

La mayor parte del trabajo que hacemos es InvariantCulture , que según mi entendimiento anterior se supone que es inmutable por diseño. Parece que el comportamiento de IndexOf es diferente entre .NET 5.0 y .NET Core 3.1 en esas circunstancias también.

¿Cómo puede ayudar cualquier analizador a los proyectos existentes?

@petarrepac eso también es algo

@isaacabraham y VB también;)

Preferiría tener una forma en tiempo de compilación de manejar esto.

@Aaronontheweb , tiene una forma en tiempo de compilación de manejar esto (al compilar aplicaciones). Puede agregar esto a su proyecto:

<ItemGroup>
  <RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="true" />
</ItemGroup>

EDITAR: esto es solo para aplicaciones que consumen una biblioteca, desafortunadamente no puede controlar esto al escribir una biblioteca.

A largo plazo, el equipo de Windows está promoviendo el cambio a ICU, por lo que en algún momento ICU será la historia de la globalización, y solo somos un pequeño envoltorio para las bibliotecas de SO.

¿Qué va a pasar cuando un .NET 5 llama a un objeto definido dentro de una biblioteca .NET Standard 2.0 que manipula una cadena que se le pasa desde el sitio de llamadas .NET? ¿Se acostumbra el antiguo comportamiento o el nuevo comportamiento?

El nuevo comportamiento se usará, porque depende del tiempo de ejecución y .NET Standard es solo un estándar para que los tiempos de ejecución lo implementen. Sin embargo, tenga en cuenta que esto trae consistencia de plataforma entre Unix y Windows, si ejecuta estas mismas bibliotecas que a la gente le preocupan en Unix, obtendrá el resultado ICU ya que ICU es el respaldo biblioteca en Unix.

@reflectronic tiene razón en todos sus puntos https://github.com/dotnet/runtime/issues/43736#issuecomment -716681586.

para comentar los resultados de @jbogard mencionados aquí https://github.com/dotnet/runtime/issues/43736#issuecomment -716527590, puede resumir los resultados comparando el comportamiento lingüístico entre Windows y ICU. Por cierto, ICU ahora es usado por muchas aplicaciones en Windows y se espera que aumente su uso. Además, estos resultados excluyen Linux con .NET Core 3.1 y versiones anteriores. que mostrará coherencia entre .NET 5.0 y versiones anteriores en Linux.

El punto sobre la biblioteca que solía funcionar se romperá, esto no es del todo cierto porque tales bibliotecas ya estaban dañadas en Linux.

¿Consideraría hacer un cambio importante para que StringComparison.Ordinal también sea la estrategia de comparación predeterminada?

Mencioné anteriormente que ya lo intentamos antes, pero el tamaño de las quejas era muy grande y no podíamos aplicarlo. Estamos buscando formas de ayudar a los desarrolladores a ser conscientes de especificar los indicadores de comparación de cadenas al llamar a las API de intercalación.

Me gano la vida haciendo bibliotecas, no aplicaciones. Preferiría tener una forma en tiempo de compilación de manejar esto.

Sí, algún analizador puede ayudar en tal caso. Por lo general, mire las llamadas de las API de intercalación y observe cuál no mostraba la intención de usar una operación ordinal o lingüística.

¿Cómo puede ayudar cualquier analizador a los proyectos existentes?

Los analizadores escanean el código y detectan las llamadas de las API de intercalación que no pasan las banderas, lo que ayudaría a analizar dichas llamadas y corregir si detectan un problema.

eso también es algo exclusivo de C #.

El cambio está dentro del tiempo de ejecución de .NET, que debería ser global y no restringido a C #.

@tarekgh ¿cómo se manifiesta un analizador de C # en una base de código VB o F #?

La mayor parte del trabajo que hacemos es InvariantCulture , que según mi entendimiento anterior se supone que es inmutable por diseño.

Críticamente, antes de este cambio, era imposible depender de esto para el código multiplataforma; cómo se comportan NLS y ICU, incluso cuando se usa una cultura invariante, no siempre es lo mismo (como lo demuestra este problema). Como dijo @tarekgh , este código ha estado actuando de manera diferente en Linux todo este tiempo. Ahora que se ha realizado este cambio, para cualquier instalación de Windows actualizada, lo que significa "cultura invariante" debería _en realidad_ ser coherente en todas las plataformas.

Esto puede ser una sorpresa, pero finalmente encontramos y corregimos docenas de errores relacionados con las diferencias de plataforma a lo largo de los años como resultado de los informes de los usuarios, como estoy seguro de que muchos otros autores de bibliotecas lo han hecho durante años y años.

No me entusiasma la perspectiva de que .NET 5 presente una nueva cosecha de incógnitas desconocidas y revise esas correcciones no solo para mis bibliotecas, sino también para nuestras dependencias posteriores. Eso es un costo económico significativo para nosotros que no crea nuevas mejoras de productividad para nuestros usuarios. Alguien en MSFT debería considerar eso en su discusión de costos / beneficios para hacer este cambio.

Editar: como, ya tengo los méritos técnicos, sí, gracias. Ayude a vender los beneficios económicos no obvios de realizar este cambio en lugar de los costos. ¿Por qué los usuarios deberían dar el salto y actualizarse a .NET 5 de todos modos, dado el nido de ratas que parece ser este problema?

@tarekgh ¿cómo se manifiesta un analizador de C # en una base de código VB o F #?

Podemos cubrir C # y VB con un solo analizador (nos esforzamos por hacer que nuestros analizadores sean independientes del idioma siempre que sea posible), pero no podemos obtener la cobertura del analizador para F # en este momento.

@Aaronontheweb cualquiera que use la funcionalidad cultural y asuma que no va a cambiar ya está roto, incluso si no hicimos estos cambios en la UCI. Mire el blog https://docs.microsoft.com/en-us/archive/blogs/shawnste/locale-culture-data-churn del equipo de Windows que dice que incluso el comportamiento de NLS está cambiando por el bien de las mejoras. Entonces, el problema aquí no se trata de mudarse a la UCI más que simplemente detectar cualquier suposición incorrecta desde la perspectiva de las aplicaciones / bibliotecas. actualizar a 5.0 es lo mismo que actualizar a otras versiones anteriores. las aplicaciones obtendrán muchas funciones nuevas y interesantes y las aplicaciones deben probarse, al igual que algunos cambios importantes entre versiones. No estoy considerando que el cambio en el comportamiento de la globalización realmente se esté rompiendo, ya que siempre decimos que la globalización puede cambiar en cualquier momento entre los sistemas operativos y las versiones del sistema operativo. Como se indicó anteriormente, tenemos el interruptor de configuración para elegir actualizar a 5.0 con seguir usando NLS. que hará que la UCI no sea realmente un factor en la decisión de actualización. ahora con ICU, las aplicaciones tendrán la oportunidad de obtener más consistencia en los sistemas operativos e incluso podrán tener más control sobre el comportamiento de globalización si deciden utilizar la ICU local de la aplicación. Estamos brindando mucho más control a las aplicaciones que antes.

Relacionado: https://github.com/dotnet/runtime/issues/43802

(Ese problema no rastrea IndexOf _per se_. Más bien, analiza las consecuencias no deseadas de la rutina Compare predeterminada a un comparador consciente de la cultura).

¿Cómo puede ayudar cualquier analizador a los proyectos existentes?

Los analizadores escanean el código y detectan las llamadas de las API de intercalación que no pasan las banderas, lo que ayudaría a analizar dichas llamadas y corregir si detectan un problema.

Supongo que se deben agregar analizadores al csproj.
Esto no sucederá automáticamente. Por lo tanto, muchos proyectos existentes se moverán a .NET 5 sin esos analizadores.
Además, como ya se mencionó, no ayudará para los proyectos de F #.

@jeffhandley gracias, eso confirma lo que ya entendí. Por lo tanto, es importante reconocer que la posible "solución alternativa" no ayudará a los usuarios de F # (un mercado pequeño pero que, no obstante, es totalmente compatible con MS como ciudadano de primera clase de .NET). No tengo idea de qué significa:

El cambio está dentro del tiempo de ejecución de .NET, que debería ser global y no restringido a C #.

No tengo idea de qué significa:
El cambio está dentro del tiempo de ejecución de .NET, que debería ser global y no restringido a C #.

Quise decir que cualquier lenguaje que use el tiempo de ejecución de .NET se verá afectado y no solo C #.

Voy a expresar enérgicamente mi opinión nuevamente de que debería haber un analizador listo para usar para descubrir estas trampas en el nuevo mundo, especialmente si el plan es cambiar el comportamiento actual.

Entiendo completamente los méritos técnicos de hacerlo y no estoy sugiriendo que no haga un cambio (a largo plazo parece que este es el movimiento correcto). Tampoco estoy diciendo que no esté ya documentado como mejor práctica. Lo que estoy diciendo es que realmente necesitamos que esto sea un error grande, rojo y parpadeante para los desarrolladores que intentan pasar a .NET 5. Fuera de la caja Los desarrolladores van a asumir que esto "simplemente funciona" de otra manera.

En este momento, puede usar esta biblioteca de Roslyn Analyzer de @meziantou para encontrar áreas afectadas: https://github.com/meziantou/Meziantou.Analyzer/tree/master/docs.

En este caso particular, esto arrojará un MA0074 - Evite métodos implícitos sensibles a la cultura

image

Dicho esto, esto realmente debe estar listo para usar, he abierto este problema de Roslyn aquí: https://github.com/dotnet/roslyn-analyzers/issues/4367

@tarekgh Gracias por aclarar. Mi punto original fue que los analizadores no son la respuesta aquí si está buscando una solución que funcione para todos los usuarios de .NET.

Estamos buscando más formas de ayudar a los desarrolladores a ser conscientes al llamar a algo como IndexOf y proporcionar intencionalmente indicadores StringComparison para expresar la intención. También agradecemos cualquier idea que se le ocurra.

¿Qué hay de desaprobar los métodos antiguos (usando un atributo [Obsolete] )?

¿Qué sucede con la compatibilidad de .NET Standard cuando la superficie de la API es la misma pero el comportamiento cambia?

Un cambio para honrar los grupos de grafemas no me molesta, siempre que el impacto esté bien documentado. Un cambio que hace que miembros estrechamente relacionados de la familia de métodos de cadena se comporten de manera incoherente entre sí.

Alguien que esté haciendo una manipulación informal de cadenas, indiferente a la oscuridad de los caracteres, los grupos de grafemas o la configuración regional, consideraría dado que si str.Contains (lo que sea) tiene éxito, no hay necesidad de inspeccionar el resultado de str.IndexOf (lo que sea ) porque nos acaban de decir que está allí y, por lo tanto, se puede encontrar. No importa qué segundo parámetro que no les interese saber es el predeterminado, porque el valor predeterminado seguramente se comportará de la misma manera en todos los métodos , liberándolos de la necesidad de estudiar todas las sutilezas para usarlos.

Inconsistencias como esta producen un lenguaje que solo los expertos pueden usar con éxito y alienan a la gente que sale del campo del código. No suba el listón para entrar de esta manera.

Estoy de acuerdo con @lupestro. La inconsistencia en el comportamiento del método es muy preocupante. Si va a tener métodos de larga data que actúan de manera diferente e inconsistente, habrá mucha tristeza. La gente se encontrará con esto, se sentirán traicionados por la API y luego se preguntarán qué otras bombas de tiempo están esperando para explotar. Muchos de los que adoptarán .NET descartarán C # por este tipo de problema. Parece que debería eliminar las sobrecargas que no aceptan una configuración regional o normalizar la configuración regional predeterminada para los métodos. Ya existe un Compare y CompareOrdinal, quizás se necesite un (Last) IndexOf (Any) y (Last) IndexOf (Any) Ordinal. No me gusta esa solución, pero al menos sería coherente con lo que existe actualmente. Quizás la idea del uso ordinal en cadena debería depreciarse. Si tengo que elegir entre rápido o correcto, elegiré "correcto" cada vez. Los comportamientos inconsistentes y no intuitivos son extremadamente frustrantes.

Veo que este problema ya está cerrado, así que supongo que ya se decidió que esto avanzará en .NET 5.0. Sé que esto es difícil y que la información de Cultura (como el Tiempo) puede cambiar por todo tipo de razones no técnicas. Los desarrolladores deben ser conscientes de esto, pero también deben depender de sus API para ser autoconsistentes. Debería haber al menos una advertencia (en 5.0) como lo señaló @aolszowka que indica un problema ... y lo que es más importante, por qué es un problema. Avanzar es importante y, a veces, eso significa que tienes que "romper" los viejos comportamientos / suposiciones. Eso no implica que sea necesario introducir nuevas inconsistencias. Este cambio rompe las expectativas y el código. Si no es posible hacer que los métodos sean consistentes, preferiría que CultureInfo sea forzado a ser explícito (que luego podría abordar a través de un método de extensión) que simplemente tener la posibilidad de un error no intuitivo que explote mi código durante un ciclo de desarrollo estresante o peor en la instalación de un cliente.

TLDR: cambie las cosas que necesitan cambiar, pero no haga que la API sea inconsistente para hacerlo. Si lo vas a romper reemplázalo por algo mejor.

He estado usando .NET durante años y siempre utilizo InvariantCulture por defecto cuando uso estas funciones. Con respecto a los idiomas distintos del inglés, siempre he sido consciente de los pares de caracteres que funcionan como alias para otras letras específicas del idioma, y ​​el trabajo adicional que implica la verificación de estos pares al hacer comparaciones con CurrentCulture como predeterminado. Esto me ha mordido, por ejemplo, al escribir código ASP.NET que establece CurrentCulture del hilo en función del idioma preferido del usuario enviado por el navegador, y las comparaciones para un usuario que no usa inglés hacen que el código se rompa de manera sutil, especialmente cuando las cadenas contienen una mezcla de texto ingresado por el usuario (lingüístico) y ordinal.

Este comportamiento de grupo de grafemas Unicode es nuevo para mí, ya que habría esperado un comportamiento ordinal para los avances de línea y los retornos de carro, incluso si hubiera usado sobrecargas invariantes como lo hago normalmente. Me parece que el comportamiento que hace más trabajo y requiere conocimiento experto debería ser el comportamiento opt-in, independientemente de la rectitud técnica. Quizás ese barco zarpó hace mucho tiempo, pero _este cambio radical_ debería hacerse de forma tan transparente como, por ejemplo, los cambios de idioma, sin tener que leer un blog oscuro. Mis colegas apenas están al tanto de las nuevas características de C # 9.0, y mucho menos de alguna regla arcana de la UCI que puede afectar el código que escriben hoy y que algún día puede ser portado y compilado en .NET 5 (o, más probablemente, en .NET 6 o 7).

Obsolar los métodos antiguos es una cosa que estamos considerando. Terminé un borrador de la propuesta anoche y lo estoy buscando para una revisión interna, y lo publicaré como un nuevo número aquí en unas horas.

El borrador se publica en https://github.com/dotnet/runtime/issues/43956. Enumera varias alternativas para posibles caminos a seguir (incluido no hacer nada) y sopesa los pros y los contras de cada enfoque. No dude en dejar sus comentarios sobre los cursos de acción propuestos allí.

Si está informando un error, presente un nuevo problema y utilice ese nuevo problema para describir el error.

Si tiene comentarios sobre este tema en particular ( "\r\n" frente a "\n" ), siga respondiendo en este hilo. ¡Gracias!

Reapertura mientras consideramos los enfoques descritos en el documento de @GrabYourPitchforks .

Lo escuchamos, hay muchos comentarios claros aquí, estamos trabajando para encontrar el camino correcto a seguir y mantendremos este problema actualizado.

Un cambio para honrar los grupos de grafemas no me molesta, siempre que el impacto esté bien documentado. Un cambio que hace que miembros estrechamente relacionados de la familia de métodos de cadena se comporten de manera incoherente entre sí.

Alguien que esté haciendo una manipulación informal de cadenas, indiferente a la oscuridad de los caracteres, los grupos de grafemas o la configuración regional, consideraría dado que si str.Contains (lo que sea) tiene éxito, no hay necesidad de inspeccionar el resultado de str.IndexOf (lo que sea ) porque nos acaban de decir que está allí y, por lo tanto, se puede encontrar. No importa qué segundo parámetro que no les interese saber es el predeterminado, porque el incumplimiento seguramente se comportará de la misma manera en todos los métodos, liberándolos de la necesidad de estudiar todas las sutilezas para usarlos.

Inconsistencias como esta producen un lenguaje que solo los expertos pueden usar con éxito y alienan a la gente que sale del campo del código. No suba el listón para entrar de esta manera.

Sí, esto expresó totalmente mis preocupaciones. Como desarrollador chino típico, rara vez ponemos StringComparison o CultureInfo explícitamente al llamar a métodos relacionados con cadenas en nuestros códigos de aplicación, y simplemente funciona. ¡Definitivamente no esperamos un comportamiento diferente entre IndexOf y Contains !
.net 5.0
image
.net core 3.1
image
.NET Framework
image

Estoy de acuerdo con @lupestro. La inconsistencia en el comportamiento del método es muy preocupante. Si va a tener métodos de larga data que actúan de manera diferente e inconsistente, habrá mucha tristeza.

Quizás un punto clave aquí es que los dos métodos siempre han sido inconsistentes. No se volvieron inconsistentes repentinamente en .NET 5.0. Si sigo las cosas correctamente, IndexOf siempre ha usado la comparación de cultura actual, Contains siempre ha usado la comparación ordinal. Por supuesto .NET 5.0 agrega más inconsistencia. Pero el error aquí fue en el diseño de la API original que permitió esta inconsistencia.

Si sigo las cosas correctamente, IndexOf siempre ha utilizado la comparación ordinal, Contains siempre ha utilizado la comparación de la cultura actual. Por supuesto .NET 5.0 agrega más inconsistencia. Pero el error aquí fue en el diseño de la API original que permitió esta inconsistencia.

Eso es correcto, pero es al revés, IndexOf(string) usa la cultura actual, IndexOf(char) usa Ordinal y Contains usa ordinal.

Explicaré brevemente las diferencias entre IndexOf y Contains las que otros aludieron recientemente.

IndexOf(string) siempre ha asumido la comparación _CurrentCulture_, y Contains(string) siempre ha asumido la comparación _Ordinal_. Esta discrepancia existía mucho tiempo atrás en .NET Framework. No es una nueva discrepancia introducida en .NET 5. Por ejemplo, en .NET Framework (que usa la función NLS de Windows), la ligadura "æ" y la cadena de dos caracteres "ae" se comparan como iguales bajo un comparador lingüístico. Esto da como resultado la siguiente discrepancia:

// Sample on .NET Framework, showing Contains & IndexOf returning inconsistent results
Console.WriteLine("encyclopædia".Contains("ae")); // prints 'False'
Console.WriteLine("encyclopædie".IndexOf("ae")); // prints '8' (my machine is set to en-US)

Esta discrepancia existe desde hace más de una década. Está documentado y hay orientación al respecto. ¿Es un mal diseño? Quizás. Estamos discutiendo en https://github.com/dotnet/runtime/issues/43956 formas de hacer que el ecosistema sea más saludable en el futuro.

Para este problema específico, lo que realmente nos estamos preguntando es: "Dado que la discrepancia ha existido desde siempre, ¿qué tan separados se permite que estos dos métodos se desvíen _en la práctica_ antes de que dañen el ecosistema más grande?" Creo que todavía estamos tratando de definir dónde debería estar ese umbral. Informes como este son extremadamente útiles porque nos brindan información sobre las personas que usan estas API en la práctica y las expectativas que tienen los clientes con respecto a su comportamiento. Como administradores del marco, debemos considerar no solo la documentación técnica, sino también la forma en que las aplicaciones del mundo real consumen estas API.

En realidad, este comentario no pretende defender ningún punto de vista en particular. Mi intención es aclarar algunos conceptos erróneos que he visto y ayudar a explicar cómo hemos enmarcado el problema.

Incluso si se especifica InvariantCulture, ¿es un comportamiento correcto que \n no coincida en la versión ICU?

image
image

¿Quizás, el siguiente código muestra 5 en Linux y -1 en Windows si usamos git y su configuración predeterminada (autocrlf = true)?

using System;

var s = @"hello
world";
Console.WriteLine(s.IndexOf("\n", StringComparison.InvariantCulture));

@ufcpp Sí, por ICU ese es el comportamiento esperado, como se discutió anteriormente en este hilo. Los dos caracteres <CR><LF> cuando se encuentran adyacentes entre sí se consideran una unidad inquebrantable para fines lingüísticos. La búsqueda de <LF> usando un comparador lingüístico (como _InvariantCulture_) no producirá ninguna coincidencia, ya que dividiría esta unidad irrompible.

Me gustaría compartir una actualización con todos: hemos decidido mantener la UCI como predeterminada para 5.0 GA. Veremos cómo mejorar la trampa de usar accidentalmente la comparación lingüística cuando se pretendía usar ordinal, que se rastrea en https://github.com/dotnet/runtime/issues/43956. También nos comunicaremos con algunas de las bibliotecas que hemos identificado como afectadas por este problema. En los próximos días, compartiremos más documentos que acompañarán a la próxima versión 5.0 que ayudarán a las personas a identificar mejor el código problemático y solucionarlo para evitar este problema.

Esta fue una decisión muy difícil de tomar y tuvimos que sopesar el impacto de la compatibilidad en el ecosistema con el impulso de la estandarización entre plataformas.

Dejaremos este problema abierto para considerar una mayor mitigación del problema \r\n en el servicio si se determina que las mitigaciones actuales son insuficientes.

También me gustaría animar a la gente a que continúe informando los problemas que se encuentren como resultado del cambio de la UCI, aunque el comportamiento pueda ser de una causa conocida, eso no significa que sea "por diseño". Continuaremos investigando las diferencias y entenderemos la causa raíz y determinaremos si necesitamos impulsar cambios en ICU o .NET para abordarlos.

Existe una regla de analizador para especificar siempre StringComparison.

https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ca1307

Me gustaría compartir una actualización con todos: hemos decidido mantener la UCI como predeterminada para 5.0 GA. Veremos cómo mejorar la trampa de usar accidentalmente la comparación lingüística cuando se pretendía usar ordinal, que se rastrea en # 43956. También nos comunicaremos con algunas de las bibliotecas que hemos identificado como afectadas por este problema. En los próximos días, compartiremos más documentos que acompañarán a la próxima versión 5.0 que ayudarán a las personas a identificar mejor el código problemático y solucionarlo para evitar este problema.

Esta fue una decisión muy difícil de tomar y tuvimos que sopesar el impacto de la compatibilidad en el ecosistema con el impulso de la estandarización entre plataformas.

Dejaremos este problema abierto para considerar una mayor mitigación del problema \r\n en el servicio si se determina que las mitigaciones actuales son insuficientes.

También me gustaría animar a la gente a que continúe informando los problemas que se encuentren como resultado del cambio de la UCI, aunque el comportamiento pueda ser de una causa conocida, eso no significa que sea "por diseño". Continuaremos investigando las diferencias y entenderemos la causa raíz y determinaremos si necesitamos impulsar cambios en ICU o .NET para abordarlos.

Me gustaría saber si todavía puedo usar dependencias de .NET Standard sobre las que no tengo control.

Sospecho que no podré hacerlo. ¿Qué tal si nos aseguramos de que las personas pasen a, no sé, .NET Standard 2.2? ¿Cambiar la API?

De modo que pueda saber con certeza que obtengo el comportamiento esperado.

No estoy seguro de qué otra manera podemos evitar romper el ecosistema actual, que ya tiene una buena cantidad de problemas.

Me alegraría que me demuestren que estoy equivocado, por favor, hágalo con prejuicio, me tranquilizaría :)

@rcollina

Me gustaría saber si todavía puedo usar dependencias de .NET Standard sobre las que no tengo control.

Las bibliotecas .Net Standard generalmente se supone que son multiplataforma. Y si alguna biblioteca funciona correctamente en .Net Core 3 en Unix hoy (que usa ICU), es casi seguro que también funcionará en .Net 5 en Windows (que también usa ICU).

.NET estándar 2.2

.Net Standard vNext existe efectivamente, aunque se llama ".Net 5.0". (Es decir, si está escribiendo una biblioteca y no le importa admitir marcos más antiguos, hoy apuntará a .Net Standard 2.1. En un mes, apuntará a .Net 5.0 en su lugar).

@svick lo entiendo. Creo que ya entiendo cómo funciona .NET Standard. Entiendo que .NET 5 es el nuevo .NET Standard, por así decirlo.

Pido disculpas, pero todavía no estoy seguro de lo que sucede cuando hago referencia a una biblioteca .NET Standard 2.1 que se basó en una inconsistencia de comportamiento preexistente entre IndexOf y Contains.

Lo que está cambiando aquí es algo fuera de banda, UCI versus NLS. Este cambio amplía la discrepancia que ya teníamos y rompe las expectativas.
No hay nada que codifique esta información en bibliotecas.

No pretendo entender todas las implicaciones, pero no puedo deshacerme de la sensación de que estamos siendo "técnicamente correctos" sin un camino de incorporación sin problemas hacia la nueva normalidad. Que es muy necesario.

Como mencionó alguien más, la mayoría de las personas ni siquiera están al tanto de las nuevas características del lenguaje, y mucho menos de los cambios sísmicos como este descubierto al azar por los mejores miembros de nuestra comunidad. ¿Cuáles son las posibilidades de que podamos soportar esto?

Me gustaría saber si todavía puedo usar dependencias de .NET Standard sobre las que no tengo control.

Lo que está cambiando aquí es algo fuera de banda, UCI versus NLS. Este cambio amplía la discrepancia que ya teníamos y rompe las expectativas.

No, no es así. No se puede enfatizar lo suficiente aquí que ICU siempre se ha utilizado en Unix. Se supone que las bibliotecas .NET Standard son portátiles por diseño y todo lo que funcionaba anteriormente en Linux .NET Core 3.x funcionará en .NET 5.

La mayor parte del trabajo que hacemos es InvariantCulture, que según mi comprensión anterior se supone que es inmutable por diseño.

No es verdad. Se supone que InvariantCulture solo ignora la configuración de idioma del usuario. Todavía puede cambiar con actualizaciones de la especificación Unicode o de las bibliotecas de globalización, etc.

¿El equipo de .NET consideró el dolor, los problemas y el costo que esto podría causar?

Comentarios como este me irritan muchísimo. Cualquier código que se rompa repentinamente con este cambio fue incorrecto para empezar. ¿Cómo se supone que la plataforma avanzará si el equipo de .NET tiene que retener el comportamiento del código de usuario que es incorrecto o se basa en detalles de implementación? No es que no proporcionaran un interruptor de compatibilidad. Una gran parte de la razón por la que .NET Core se separó de .NET Framework fue resolver este problema a través de características como instalaciones en paralelo e implementaciones en tiempo de ejecución locales de aplicaciones. Si no puede pasar a .NET 5 debido a esto, no cambie a .NET 5.

No me entusiasma la perspectiva de que .NET 5 presente una nueva cosecha de incógnitas desconocidas y revise esas correcciones no solo para mis bibliotecas, sino también para nuestras dependencias posteriores.

Si ha eliminado todos los errores de diferencia de plataforma como afirma, entonces no debería tener nada de qué preocuparse.

¿El equipo de .NET consideró el dolor, los problemas y el costo que esto podría causar?

Comentarios como este me irritan muchísimo.

Millones de proyectos se ejecutan en .NET y la manipulación de cadenas es una operación muy frecuente.
El esfuerzo que requiere el equipo de .NET para arreglar / cambiar esto es minúsculo en comparación con el esfuerzo necesario para verificar todo el código existente que se migrará a .NET 5/6.

Por lo tanto, es bastante justo preguntar sobre el "plan" para abordar esto.
¿Hay algún plan?
¿Se estimó el efecto de este cambio?
¿Es el 0,001% de todos los proyectos? ¿Es el 75%?
¿Cuáles son otros cambios similares que desconocemos?

Quizás esto afecte solo a una pequeña cantidad de proyectos.
Pero, ¿fue estimado?

Por cierto, estoy a favor de romper los cambios por una buena razón. Pero también necesitamos una ruta de migración que no sea demasiado riesgosa.

@petarrepac No me malinterpretes, lo entiendo. Pero como se ha señalado varias veces en este hilo:

  1. Hay un plan y es proporcionar un conmutador de configuración de tiempo de ejecución.
  2. El comportamiento que se dice que se está rompiendo es el comportamiento existente de .NET en todas las plataformas que no son Windows.
  3. Esto solo debería afectar al código que realiza operaciones sensibles a la cultura donde se pretendía ordinal.

Dados los dos últimos puntos, probablemente sea razonable asumir que esto afecta a un porcentaje bastante pequeño de proyectos.

Es 100% justo preguntar acerca de esto, pero las personas que escriben comentarios como el que cité a menudo simplemente asumen que no se puso ni se escribió ninguna consideración antes de tratar de comprender el panorama general detrás del cambio.

Hola a todos. Queríamos dar un breve resumen de las acciones que tomamos cuando se abrió este problema y, al final, por qué decidimos mantener la actualización predeterminada de Windows 10 de mayo de 2019 o posterior para ser ICU para .NET 5.0.

Cuando se abrió el problema, comenzamos algunas discusiones internas sobre el impacto potencial y el dolor que esto podría haber tenido con nuestros clientes dada la inconsistencia entre Contains(string) que es Ordinal y IndexOf(string) teniendo en cuenta la cultura, además de tener otras API que sean conscientes de la cultura de forma predeterminada cuando se opera sobre una cadena, pero siendo Ordinal cuando se opera sobre Span<char> o ReadOnlySpan<char> . Entonces, después de discutir este tema, comenzamos a analizar los paquetes NuGet que podrían verse afectados y recopilamos datos para obtener una imagen clara. Estos fueron nuestros hallazgos:

  • "\ n" es el número 30 en los "literales de cadena más utilizados" que se pasan a IndexOf, LastIndexOf, EndsWith, StartsWith, Compare y CompareTo.
  • El 1% de los sitios de llamada a String.EndsWith tienen una cadena de búsqueda que comienza con \ n. Cualquiera de estos sitios de llamada donde la cadena que se está probando contiene "finales de línea estilo Windows" se rompería.
  • Hay ID de paquetes 2040 alojados en NuGet.org, donde una versión contiene un ensamblado con un sitio de llamadas en riesgo.
  • La gran mayoría de estos sitios de llamada son para EndsWith e IndexOf, lo que es consistente con la hipótesis de que este patrón se usa a menudo para algoritmos ingenuos de salto de línea.

De estos paquetes de 2040 que tenían un sitio de llamadas en riesgo, solo 539 admiten alguna versión de .NET Standard o .NET Core, por lo que es probable que solo el 0,54% de los paquetes enumerados en NuGet.org estén expuestos a la ruptura.

Analizamos los paquetes en la lista de 539 identificadores de paquetes potencialmente afectados para tener una idea del impacto real en ellos. Observamos los 70 principales (por recuento de descargas), 20 no expusieron el patrón en la última versión, de los que expusieron el patrón, solo pudimos ver 32 que tenían una licencia permisiva:

  • 14 no estaban sujetos al error
  • 13 estaban potencialmente rotos
  • 5 eran inciertos (no hubo una indicación clara de roturas debido a la codificación defensiva para diversos patrones de rotura de línea u otras mitigaciones).

Esto significa que, de los 70 paquetes principales que se descargaron, solo el 18% se vieron potencialmente afectados.

Estos porcentajes se apilan y no contra el número total de paquetes en NuGet.org, que es 229,536. Entonces, si usáramos el número total de paquetes y el número total de descargas en NuGet.org, estaríamos viendo 539 paquetes potencialmente afectados de 229,536 que es 0.24%.

Y aunque es genial para nosotros analizar bibliotecas, nuget representa solo una pequeña fracción del código C # que existe. E incluso si alguien es el propietario del código, a) los errores pueden no ser fáciles de rastrear yb) es posible que ya no tengan la fuente.

Sin embargo, esta fue una buena fuente de datos para concluir que, si bien esto podría ser un cambio de comportamiento muy notable, es una interrupción que ya sucedió en Unix al leer entradas que podrían contener finales de línea de Windows (que podrían no ser tan comunes).

En .NET Core y .NET 5+, nos esforzamos por lograr la coherencia en todos los sistemas operativos y, dado el impacto de este cambio, se sintió como lo correcto. Nos preocupamos por la compatibilidad y, por lo tanto, proporcionamos un conmutador de tiempo de ejecución de compatibilidad para que las personas puedan volver al comportamiento heredado.

Además, una conclusión de los paquetes que pudimos inspeccionar, dado que el comportamiento ya es diferente en Unix, vimos mucha programación defensiva contra este problema, para mitigar posibles interrupciones en los sistemas operativos.

Además, la globalización puede cambiar en cualquier momento, ya que somos una envoltura delgada en todo el sistema operativo, por lo que se sintió como lo correcto en este momento ser la misma envoltura en todos los sistemas operativos que admitimos.

Como parte de esto, mejoramos nuestra documentación con ejemplos prácticos, las reglas del analizador de roslyn y cómo el código afectado puede mitigar esto.

Gracias por todos sus valiosos comentarios, ya que siempre nos lleva a un lugar mejor e intentaremos seguir mejorando esta experiencia para .NET 6, como se explica en: https://github.com/dotnet/runtime/issues/43956

Dado que entendemos el dolor que esto puede causar debido a las diferencias entre los finales de línea en Unix y Windows, mantendremos este problema abierto e investigaremos una posible forma de mitigar el caso \r\n para .NET 5.0 .x que debería formar parte de una versión de servicio.

También hay una diferencia con char y string:

Console.WriteLine("com/test/test/test/a/b/ʹ$ʹ".IndexOf("$"));
Console.WriteLine("com/test/test/test/a/b/ʹ$ʹ".IndexOf('$'));
-1
24

@mattleibow cuando usa la búsqueda de caracteres, realiza una búsqueda ordinal. El documento https://docs.microsoft.com/en-us/dotnet/api/system.string.indexof?view=net-5.0#System_String_IndexOf_System_Char_ que indica This method performs an ordinal (culture-insensitive) search . Si realiza la búsqueda de cadenas con la opción ordinal, obtendrá el mismo resultado que el carácter.

C# Console.WriteLine("com/test/test/test/a/b/ʹ$ʹ".IndexOf("$", StringComparison.Ordinal));

~ Parece que CA1307 solo se activa en indexof(char) pero no en indexof(string) . ¿Existe una regla para la versión string ? Esto parece más importante ya que el valor predeterminado de char usa automáticamente ordinal y el cambio de ruptura realmente no afecta esto, mientras que el comportamiento de string ha cambiado y necesita especificar ordinal para restaurar el comportamiento algunos casos. ~

~ ¿Cómo detecta indexof(string) ? ~

Lo encontré, es la regla CA1310. Nuestros documentos son incorrectos para https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization-icu#use -nls-place-of-icu y no mencionan esta variación específica. Actualizaré esos documentos.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

yahorsi picture yahorsi  ·  3Comentarios

v0l picture v0l  ·  3Comentarios

omajid picture omajid  ·  3Comentarios

EgorBo picture EgorBo  ·  3Comentarios

aggieben picture aggieben  ·  3Comentarios