Runtime: Критическое изменение с помощью string.IndexOf (string) из .NET Core 3.0 -> .NET 5.0

Созданный на 22 окт. 2020  ·  76Комментарии  ·  Источник: dotnet/runtime

Описание

Я расширяю пакет для поддержки .NET 5.0 и столкнулся с критическим изменением. Учитывая консольное приложение:

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

Я получаю разные результаты в зависимости от среды выполнения из .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

Конфигурация

Windows 10 Pro, сборка 19041 x64
.NET Core 3.1.9
.NET 5.0.0-rc.2.20475.5

Регресс?

Да, работал через .NET Core 3.1.9

area-System.Globalization question

Самый полезный комментарий

@tarekgh , я согласен с тем, что разные результаты между Contains и IndexOf сами по себе не являются проблемой.

Проблема явно в том, что IndexOf не может найти одну строку только ASCII внутри другой строки только ASCII (я не уверен, что когда-либо было какое-либо зависящее от языкового стандарта поведение, наложенное на строки, содержащие только ASCII!).

Это не то, чего я ожидал бы от любых изменений, связанных с языковым стандартом / NLS / ICU; фактически, я не мог представить себе, чтобы какой-либо другой язык программирования / среда выполнения вел себя так.

Вот упрощенный тестовый пример, сломанный (я имею в виду, дающий мне совершенно неожиданный результат) на .NET 5 RC 2:

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

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

Должно ли это действительно так работать? Кроме того, почему? Что он на самом деле пытается делать?

Было ли это действительно запланированное изменение?

Да, переход на ICU - это преднамеренное изменение по разным причинам.

Извините, но я не верю, что это было запланированное изменение, поэтому я хотел бы подчеркнуть: я не мог представить, чтобы кто-нибудь _планировал_ такое изменение. Например, люди из команды .NET сидели вместе и обсуждали:

Строка «\ n \ r \ nTest» содержит «\ nTest» с включенным ICU? Нет, явно нет!

И никто не жаловался? Ни единого шанса!

Это не похоже на запланированное или ожидаемое изменение, а вместо этого выглядит очень серьезной ошибкой, большим препятствием для совместимости. Из-за этого новые и перенесенные приложения .NET не будут работать должным образом в новой среде выполнения, потому что они не смогут найти подстроки внутри строки!

Почему ICU вообще заботится о окончании строк? Есть ли у некоторых языков собственные окончания строк, зависящие от локали?

PS Да, вы могли бы возразить, что действительно всегда следует называть какой-то вариант независимого от культуры IndexOf , например, с порядковым флагом. Но если вы решили сломать это _это_ сильно в .NET 5, не могли бы вы тогда просто заставить его использовать нормальное порядковое значение по умолчанию? Я думаю, что это сломает меньше приложений, чем текущее изменение, которое мы наблюдаем в .NET 5 RC 2.

Кроме того, я думаю, мы все понимаем, что, несмотря на то, что IndexOf всегда ведет себя зависящим от культуры образом, в «дикой природе» есть _tons_ кода, которые используют IndexOf без порядковых флагов, и этот код _используется для работы_ (по крайней мере, в некоторых / большинстве случаев). И он перестанет работать после обновления .NET 5.

Все 76 Комментарий

@tarekgh

Отметка подписчиков в этой области: @tarekgh , @safern , @krwq
Смотрите информацию на сайте area-owners.md, если хотите подписаться.

Это сделано намеренно, поскольку в .NET 5.0 мы перешли на использование ICU вместо NLS. Вы можете посмотреть https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization-icu для получения дополнительных сведений.

У вас есть возможность использовать переключатель конфигурации System.Globalization.UseNls чтобы вернуться к старому поведению, но мы не рекомендуем этого делать, поскольку ICU более правильная, а дальнейшее использование ICU обеспечит согласованность между ОС.

забыл сказать, что если вы хотите, чтобы IndexOf вел себя как Contains , тогда вам следует использовать порядковые сравнения.

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

У вас есть возможность использовать переключатель конфигурации System.Globalization.UseNls, чтобы вернуться к старому поведению, но мы не рекомендуем этого делать, поскольку ICU более правильная, а дальнейшее использование ICU обеспечит согласованность между ОС.

Да, если вы запустите этот код в Unix с таргетингом на netcoreapp3.1 вы увидите такое же поведение:

 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

и как @tarekgh при Ordinal возвращает ожидаемый результат.

 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

Я думаю, что это не удается, потому что смесь \r\n и \n в исходной строке. Если я заменю все экземпляры \r\n на \n это сработает. То же самое верно, если я сделаю все \r\n . Это просто сочетание, которое дает результаты, отличные от результатов сравнения ICU.

Проблема, о которой сообщалось в Twitter, заключалась в том, что во время выполнения 5.0 повторные вызовы string.IndexOf с одними

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

Изменить: выше было недоразумение.

@GrabYourPitchforks, можем ли мы обновить название проблемы? Поскольку это принципиальное техническое изменение, но такое случается в Windows и Unix ... верно?

Я связался с Джимми офлайн, чтобы получить разъяснения. Возможно, я неправильно понял его исходный отчет о проблеме. 280-символьные форумы не всегда эффективны для четкого сообщения об ошибках. ;)

Чтобы уточнить, Contains API выполняет порядковую операцию. IndexOf без каких-либо флагов сравнения строк является лингвистической операцией, а не порядковым номером. Если вы хотите сравнить поведение Contains с IndexOf, необходимо использовать IndexOf(expected, StringComparison.Ordinal) .
Если вам нужно узнать больше о разнице, https://docs.microsoft.com/en-us/dotnet/csharp/how-to/compare-strings - полезная ссылка.

Я получил разъяснения в Твиттере. Приложение не вызывает IndexOf в цикле. Это просто стандартный отчет о поведенческих различиях 3.0 и 5.0.

@GrabYourPitchforks не могли бы вы поделиться ссылкой https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization-icu в своих ответах в твиттере и упомянуть, что у нас есть переключатель конфигурации для возврата к старому поведению?

Я получил разъяснения в Твиттере. Приложение не вызывает IndexOf в цикле. Это просто стандартный отчет о поведенческих различиях 3.0 и 5.0.

Спасибо, @GrabYourPitchforks ... на основе этого, закрывая его как задумано.

Чтобы добавить сюда больше, если вы хотите получить старое поведение без возврата к NLS, вы можете сделать

`` С #
CultureInfo.CurrentCulture.CompareInfo.IndexOf (фактический, ожидаемый, CompareOptions.IgnoreSymbols)


or 

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

вместо

C# actual.IndexOf(expected)

и вы должны получить желаемое поведение.

Я ничего не вижу о \r\n vs \n в связанной документации по ICU (https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/ глобализация-icu).

Было ли это действительно запланированное изменение?

@ForNeVeR будет сложно перечислить все различия между ICU и NLS. в документе говорится о главном изменении перехода на ICU. Как я уже указывал ранее, неправильно сравнивать результаты Contains с IndexOf без параметров StringComparison. Я перечислил выше несколько способов, с помощью которых вы можете получить предыдущее поведение, если хотите. Из отчета об этой проблеме мне кажется, что использование IndexOf должно использовать опцию Ordinal, и в таком случае неправильно использовать лингвистические сравнения. Использование лингвистического сравнения в таком случае может зависеть от текущей культуры, что позволяет давать разные результаты в разных средах.

Было ли это действительно запланированное изменение?

Да, переход на ICU - это преднамеренное изменение по разным причинам. В настоящее время Windows продвигает использование ICU вместо NLS. В любом случае, за ICU будущее. Кроме того, ICU даст возможность иметь согласованное поведение в Windows / Linux / OSX или на любых поддерживаемых платформах. Использование ICU даст приложениям возможность настраивать поведение глобализации, если они того пожелают.

Как указано в документе, у вас все еще есть возможность вернуться к старому поведению, если хотите.

Ой, в упомянутом документе говорится, что поведение ICU / NLS в Windows может автоматически переключаться в зависимости от доступности icu.dll в реальной среде. Это может стать большим сюрпризом для опубликованных автономных приложений. Я ожидал, что .NET отправит ICU для решения этой проблемы, если будет решено переключение, и поскольку ICU доступен не во всех целевых средах. Эта необязательная зависимость времени выполнения делает все еще смешнее.

Я ожидал, что .NET отправит ICU для решения этой проблемы, если будет решено переключение, и поскольку ICU доступен не во всех целевых средах. Эта необязательная зависимость времени выполнения делает все еще смешнее.

ICU теперь публикуется как пакет NuGet. Приложения могут использовать такие пакеты, чтобы автономное приложение гарантировало наличие ICU. посмотрите раздел app-local в документе . Короче говоря, приложение полностью контролирует желаемое поведение.

@tarekgh , я согласен с тем, что разные результаты между Contains и IndexOf сами по себе не являются проблемой.

Проблема явно в том, что IndexOf не может найти одну строку только ASCII внутри другой строки только ASCII (я не уверен, что когда-либо было какое-либо зависящее от языкового стандарта поведение, наложенное на строки, содержащие только ASCII!).

Это не то, чего я ожидал бы от любых изменений, связанных с языковым стандартом / NLS / ICU; фактически, я не мог представить себе, чтобы какой-либо другой язык программирования / среда выполнения вел себя так.

Вот упрощенный тестовый пример, сломанный (я имею в виду, дающий мне совершенно неожиданный результат) на .NET 5 RC 2:

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

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

Должно ли это действительно так работать? Кроме того, почему? Что он на самом деле пытается делать?

Было ли это действительно запланированное изменение?

Да, переход на ICU - это преднамеренное изменение по разным причинам.

Извините, но я не верю, что это было запланированное изменение, поэтому я хотел бы подчеркнуть: я не мог представить, чтобы кто-нибудь _планировал_ такое изменение. Например, люди из команды .NET сидели вместе и обсуждали:

Строка «\ n \ r \ nTest» содержит «\ nTest» с включенным ICU? Нет, явно нет!

И никто не жаловался? Ни единого шанса!

Это не похоже на запланированное или ожидаемое изменение, а вместо этого выглядит очень серьезной ошибкой, большим препятствием для совместимости. Из-за этого новые и перенесенные приложения .NET не будут работать должным образом в новой среде выполнения, потому что они не смогут найти подстроки внутри строки!

Почему ICU вообще заботится о окончании строк? Есть ли у некоторых языков собственные окончания строк, зависящие от локали?

PS Да, вы могли бы возразить, что действительно всегда следует называть какой-то вариант независимого от культуры IndexOf , например, с порядковым флагом. Но если вы решили сломать это _это_ сильно в .NET 5, не могли бы вы тогда просто заставить его использовать нормальное порядковое значение по умолчанию? Я думаю, что это сломает меньше приложений, чем текущее изменение, которое мы наблюдаем в .NET 5 RC 2.

Кроме того, я думаю, мы все понимаем, что, несмотря на то, что IndexOf всегда ведет себя зависящим от культуры образом, в «дикой природе» есть _tons_ кода, которые используют IndexOf без порядковых флагов, и этот код _используется для работы_ (по крайней мере, в некоторых / большинстве случаев). И он перестанет работать после обновления .NET 5.

Проблема явно в IndexOf, который не может найти одну строку только ASCII внутри другой строки только ASCII (я не уверен, что когда-либо было какое-либо зависящее от языкового стандарта поведение, наложенное на строки только ASCII!).

Неверно, что ASCII не зависит от локали. посмотрите ссылку http://userguide.icu-project.org/collation/concepts в качестве примера того, как поведение символов ASCII может отличаться для разных культур.

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.

Кроме того, я хочу уточнить, что ICU выбирает свои данные и поведение из стандарта Unicode, который хорошо продуман многими экспертами. @GrabYourPitchforks опубликует более подробную информацию о случае \r\n\ мы говорим здесь. а пока вы можете ознакомиться с документом https://unicode.org/reports/tr29/, особенно в разделах, в которых упоминается следующее:

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)

Почему ICU вообще заботится о окончании строк? Есть ли у некоторых языков собственные окончания строк, зависящие от локали?

Об этом говорится в последнем абзаце.

Извините, но я не верю, что это было запланированное изменение, поэтому я хотел бы подчеркнуть: я не мог представить, чтобы кто-то планировал такое изменение. Например, люди из команды .NET сидели вместе и обсуждали:
Это не похоже на запланированное или ожидаемое изменение, а вместо этого выглядит очень серьезной ошибкой, большим препятствием для совместимости. Из-за этого новые и перенесенные приложения .NET не будут работать должным образом в новой среде выполнения, потому что они не смогут найти подстроки внутри строки!

Это хорошо спланировано, сработано и глубоко задумано. вы можете посмотреть выпуск https://github.com/dotnet/runtime/issues/826, который мы опубликовали давно и опубликовали для всех.
Я также хочу подчеркнуть, что поведение глобализации может измениться в любое время не только для .NET, но и для ОС и других платформ. Вот почему мы поддерживаем локальную функцию приложения ICU, чтобы приложения могли использовать определенную версию ICU, чтобы гарантировать, что поведение, которое они используют, не изменится. Другое дело, что сама Windows находится в процессе продвижения использования ICU, и однажды поведение ICU станет тем, что будет использовать большинство пользователей.

как с порядковым флагом. Но если вы решили так сильно сломать его в .NET 5, не могли бы вы просто заставить его использовать нормальное порядковое значение по умолчанию? Я думаю, что это сломает меньше приложений, чем текущее изменение, которое мы наблюдаем в .NET 5 RC 2.

На самом деле мы уже пытались сделать Порядковое поведение по умолчанию во время выпусков Silverlight, и это вызывало гораздо больше проблем, чем то, о чем сообщалось здесь. Мы ищем другие способы помочь разработчикам быть осознанными при вызове чего-то вроде IndexOf и намеренно предоставлять флаги StringComparison для выражения намерения. Мы также приветствуем любые идеи, которые могут возникнуть у вас.

Кроме того, я думаю, что все мы понимаем, что, несмотря на то, что IndexOf всегда ведет себя в соответствии с культурой, в дикой природе существует множество кода, который использует IndexOf без порядковых флагов, и этот код работал (в некоторых / большинстве случаев как минимум). И он перестанет работать после обновления .NET 5.

Вот почему мы предоставляем переключатель конфигурации, чтобы вернуться к старому поведению, если вы тоже захотите. посмотрите System.Globalization.UseNls

@tarekgh , спасибо за подробное объяснение!

На данный момент я думаю, что мне лучше дождаться подробностей об этом конкретном поведении \r\n . Мне непонятно, как IndexOf использует «Граничные правила графемного кластера» (и должен ли он это делать) при выполнении этого конкретного поиска).

Кроме того, даже если здесь уместна спецификация Unicode (что вполне может быть!), Прочитав https://unicode.org/reports/tr29/ , я не уверен, что она запрещает сопоставление с последним \n . Когда я читал спецификацию, там написано CR | × | LF , где × означает «Без границ (не допускайте разрывов здесь)». Итак, при разрыве последовательности \r\n\n запрещается ставить «разрыв» только между первым и вторым символами, но это нормально, если поместить «разрыв» перед третьим, не так ли? Итак, я читаю \r\n\n как два отдельных «кластера графемы», и, даже если IndexOf должен соответствовать только полным кластерам графемы и никогда не касаться частей кластеров, он все равно должен найти подстроку \nTest внутри строки \n\r\nTest .

Кроме того, вы говорите, что другие среды выполнения / языки программирования, основанные на спецификации ICU и / или Unicode, должны вести себя так же в этом конкретном примере?

Мы ищем другие способы помочь разработчикам быть осознанными при вызове чего-то вроде IndexOf и намеренно предоставлять флаги StringComparison для выражения намерения. Мы также приветствуем любые идеи, которые могут возникнуть у вас.

_ (Необходимый отказ от ответственности : я работаю в JetBrains над несколькими проектами, включая ReSharper.) _

Изначально я не хотел, чтобы этот момент не звучал как реклама, но я думаю, что это очень актуально, так что мне придется это сделать. ReSharper по умолчанию покажет следующее предупреждение для кода пользователя, вызывающего IndexOf :
image

_ (обратите внимание, что я не знал об этой конкретной диагностике ReSharper до того, как участвовал в этом потоке, поэтому я не участвую здесь только для того, чтобы привести этот аргумент) _

Итак, я думаю, что было бы очень хорошей идеей показывать такое уведомление по умолчанию и в любом другом инструменте, или, возможно, даже полностью отказаться от этого фиктивного метода с таким уведомлением.

@Никогда

Кроме того, даже если здесь уместна спецификация Unicode (что вполне может быть!), Читая unicode.org/reports/tr29, я не уверен, что это запрещает совпадение с этим последним \ n. Когда я читал спецификацию, там написано CR | × | LF, где × означает «Без границ (здесь не допускается разрыв)». Итак, при разрыве последовательности \ r \ n \ n запрещается ставить «разрыв» только между первым и вторым символами, но это нормально, если поставить «разрыв» перед третьим, не так ли? Итак, я читаю \ r \ n \ n как два отдельных «кластера графемы»

Это правильно. \r\n\n будет 2 кластера как \r\n и \n .

и даже если IndexOf должен соответствовать только полным кластерам графемы и никогда не касаться частей кластеров, он все равно должен найти подстроку \ nTest внутри строки \ n \ r \ nTest.

Это неверно. \n\r\nTest будет разделен на части. \n , \r\n и Test . очевидно, что \nTest не может быть частью этой строки. подумайте о замене кластера \r\n некоторым символом X . теперь исходной строкой будет \nXTest которая не содержит \nTest .

Кроме того, вы говорите, что другие среды выполнения / языки программирования, основанные на спецификации ICU и / или Unicode, должны вести себя так же в этом конкретном примере?

если используется уровень силы сопоставления по умолчанию, то ответ - да. ICU может позволить изменить уровень силы, что может повлиять на результат. Например, как я упоминал ранее, сделать что-то вроде:

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

изменит уровень силы и заставит операцию игнорировать символы (что изменит поведение \n и \r поскольку в это время оно будет проигнорировано)

Кроме того, я написал чистое родное приложение C для ICU и запустил тот же случай:

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

это приложение выведет результаты:

search result = -1
search result = 1

что идентично поведению, которое вы наблюдаете с .NET.

На данный момент я думаю, что мне лучше дождаться подробностей об этом конкретном \ r \ n поведении. Мне не ясно, как IndexOf использует «Граничные правила кластера графемы» (и должен ли он это делать) при выполнении этого конкретного поиска).

Конечно, кластеризация влияет на операцию сопоставления. Если вы посмотрите на http://unicode.org/reports/tr29/tr29-7.html , там ясно сказано следующее:

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. Границы кластера графемы важны для сопоставления , 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.

Я не уверен, что будет больше подробностей, но я позволю @GrabYourPitchforks прокомментировать, если у него есть что добавить сюда.

Итак, я думаю, что было бы очень хорошей идеей показывать такое уведомление по умолчанию и в любом другом инструменте, или, возможно, даже полностью отказаться от этого фиктивного метода с таким уведомлением.

Благодаря!
Это то же самое направление, в котором мы думаем.

Ради себя я сравнил различные перегрузки между версиями:

| Метод | netcoreapp3.1 | net5.0 |
| ------------------------------------------------- --------------- | ----------------- | ---------- |
| actual.Contains(expected) | Правда | Правда |
| actual.IndexOf(expected) | 1475 | -1 |
| actual.Contains(expected, StringComparison.CurrentCulture) | Правда | Ложь |
| actual.IndexOf(expected, StringComparison.CurrentCulture) | 1475 | -1 |
| actual.Contains(expected, StringComparison.Ordinal) | Правда | Правда |
| actual.IndexOf(expected, StringComparison.Ordinal) | 1475 | 1475 |
| actual.Contains(expected, StringComparison.InvariantCulture) | Правда | Ложь |
| actual.IndexOf(expected, StringComparison.InvariantCulture) | 1475 | -1 |

Пожалуйста, включите для этого анализатор.

Это похоже на одно из тех изменений, которые хоть и хороши в долгосрочной перспективе, но создают огромный отток после запуска .NET 5. Итак, если поведение этих методов отличается в .NET 5 и .NET Core 3.1, что произойдет, когда .NET 5 вызовет объект, определенный в библиотеке .NET Standard 2.0, который управляет переданным ему string с сайта .NET? Привыкает ли старое поведение или новое?

@Aaronontheweb новое поведение. Сначала я увидел это из утверждения в NUnit3, которое нацелено на netstandard2.0 . После обновления мой тест начал давать сбой, когда я только изменил целевую платформу.

Это не очень хорошо - не могу контролировать, какие старые библиотеки, на которые я ссылаюсь, делают, если я хочу обновиться.

Сколько приложений не обнаружат это в модульных тестах и ​​не запустят в производство?
Учитывала ли команда .NET боль, проблемы и расходы, которые это могло вызвать?

Это не очень хорошо - не могу контролировать, какие старые библиотеки, на которые я ссылаюсь, делают, если я хочу обновиться.

хорошая ловушка!
счастливая трата времени на охоту на странных жуков 🎉
но почему InvariantGlobalization не помогает с этой проблемой?

Пожалуйста, включите для этого анализатор.

Да уж. И не забывайте о поддержке F #.

Сколько приложений не обнаружит это в модульных тестах

Ноль - поскольку модульные тесты не предназначены для тестирования внешних библиотек / фреймворков, таких как сам .NET BCL.

Я считаю, что должен быть атрибут уровня сборки, который может управлять режимом изменения этого поведения. По крайней мере, тогда вы можете отказаться от него на уровне сборки. Это значит, что проблема .NET Standard тоже исчезнет.

Это не очень хорошо - не могу контролировать, какие старые библиотеки, на которые я ссылаюсь, делают, если я хочу обновиться.

Я не понимаю, почему ты не можешь это контролировать. Все сравнения строк выполняются с использованием ICU или NLS. Вы можете отказаться от ICU, используя переключатель совместимости, если хотите, и все ваши библиотеки вернутся к старому поведению.

Нельзя полагаться на то, что данные глобализации останутся стабильными с течением времени. Команда Windows считает, что они не боятся сломать людей, которые зависят от стабильных данных глобализации. Функции глобализации должны быть черным ящиком; Я не думаю, что для библиотек (особенно тех, которые нацелены на .NET Standard) есть смысл говорить, что они зависят от подобных деталей реализации.

Я уверен, что столько же людей жаловались на функции глобализации .NET, возвращающие разные результаты в Windows и Linux (и, возможно, еще больше людей еще даже не заметили). Лучше унифицировать поведение между Windows и другими платформами, и любой правильный код не должен полагаться на неизменность данных глобализации, независимо от того.

Не могли бы вы внести критическое изменение, чтобы сделать стратегию сравнения по умолчанию StringComparison.Ordinal ? Поскольку глобализация настолько нестабильна, имеет смысл, по крайней мере, в реализации по умолчанию использовать вместо нее стабильный алгоритм. Готов поспорить, что 99,9% людей, которые используют string.Equals(...) или string.Contains(...) и т. Д., Не передавая StringComparison , не делают это с явным намерением справиться со странными причудами, связанными с локации.

Изменить: я думаю, на мой вопрос уже был дан ответ:

На самом деле мы уже пытались сделать Порядковое поведение по умолчанию во время выпусков Silverlight, и это вызывало гораздо больше проблем, чем то, о чем сообщалось здесь. Мы ищем другие способы помочь разработчикам быть осознанными при вызове чего-то вроде IndexOf и намеренно предоставлять флаги StringComparison для выражения намерения. Мы также приветствуем любые идеи, которые могут возникнуть у вас.

Вы можете отказаться от ICU, используя переключатель совместимости, если хотите, и все ваши библиотеки вернутся к старому поведению.

Я делаю библиотеки, а не приложения. Я бы предпочел иметь способ справиться с этим во время компиляции.

Большая часть работы, которую мы выполняем, - это InvariantCulture , который, согласно моему предыдущему пониманию, должен быть неизменным по дизайну. Похоже, что поведение IndexOf отличается в .NET 5.0 и .NET Core 3.1 и в этих обстоятельствах.

Чем может помочь любой анализатор для существующих проектов?

@petarrepac , это тоже только C #.

@isaacabraham и VB тоже;)

Я бы предпочел иметь способ справиться с этим во время компиляции.

@Aaronontheweb у вас есть способ справиться с этим во время компиляции (при компиляции приложений). Вы можете добавить это в свой проект:

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

РЕДАКТИРОВАТЬ: это только для приложений, использующих библиотеку, к сожалению, вы не можете контролировать это при написании библиотеки.

В конечном итоге команда Windows продвигает переход на ICU, так что в какой-то момент ICU станет историей глобализации, а мы просто тонкая оболочка для библиотек ОС.

что произойдет, когда .NET 5 вызовет объект, определенный в библиотеке .NET Standard 2.0, который манипулирует строкой, переданной ему с сайта вызова .NET? Привыкает ли старое поведение или новое?

Новое поведение будет использоваться, потому что оно зависит от среды выполнения, а .NET Standard - это просто стандарт для реализации среды выполнения. Однако обратите внимание, что это обеспечивает согласованность платформ между Unix и Windows, если вы запустите те же библиотеки, которые беспокоят людей в Unix, вы получите результат ICU поскольку ICU является поддержкой библиотека в Unix.

@reflectronic прав во всем https://github.com/dotnet/runtime/issues/43736#issuecomment -716681586.

чтобы прокомментировать результаты @jbogard, упомянутые здесь https://github.com/dotnet/runtime/issues/43736#issuecomment -716527590, вы можете просто суммировать результаты, сравнивая лингвистическое поведение между Windows и ICU. Кстати, ICU сейчас используется многими приложениями в Windows, и ожидается, что его использование будет расти. Кроме того, эти результаты не включают Linux с .NET Core 3.1 и ниже. который покажет согласованность между .NET 5.0 и предыдущими версиями в Linux.

Пункт о библиотеке, которая раньше работала, будет нарушена, это не совсем так, потому что такие библиотеки уже были сломаны в Linux.

Не могли бы вы внести критическое изменение, чтобы также сделать StringComparison.Ordinal стратегией сравнения по умолчанию?

Я упоминал ранее, что мы уже пробовали это раньше, но количество жалоб было очень большим, и мы не могли это применить. Мы ищем способы помочь разработчикам осознанно указывать флаги сравнения строк при вызове API-интерфейсов сопоставления.

Я делаю библиотеки, а не приложения. Я бы предпочел иметь способ справиться с этим во время компиляции.

Да, в этом случае может помочь какой-нибудь анализатор. обычно смотрят на вызовы API-интерфейсов сопоставления и смотрят, какой из них не показывает намерение использования порядковых или лингвистических операций.

Чем может помочь любой анализатор для существующих проектов?

Анализаторы сканируют код и обнаруживают вызовы API-интерфейсов сопоставления, которые не передают флаги, могут помочь просмотреть такие вызовы и исправить, если обнаружена проблема.

это тоже относится только к C #.

Изменение происходит внутри среды выполнения .NET, которая должна быть глобальной и не ограничиваться C #.

@tarekgh как анализатор C # проявляет себя в кодовой базе VB или F #?

Большая часть работы, которую мы выполняем, - это InvariantCulture , который, согласно моему предыдущему пониманию, должен быть неизменным по дизайну.

Крайне важно, что до этого изменения было невозможно фактически зависеть от этого для кросс-платформенного кода; поведение NLS и ICU, даже при использовании инвариантной культуры, не всегда одинаково (о чем свидетельствует эта проблема). Как сказал @tarekgh , этот код все время действовал в Linux по-другому. Теперь, когда это изменение было внесено, для любой установки последней версии Windows, что означает «инвариантная культура», должно _ фактически_ быть единообразным на всех платформах.

Это может стать сюрпризом, но в конечном итоге мы нашли и исправили десятки ошибок, связанных с различиями платформ за эти годы в результате отчетов пользователей, как, я уверен, многие другие авторы библиотек делали годами.

Я просто не в восторге от перспективы того, что .NET 5 представит новый урожай неизвестных неизвестных и пересмотрит эти исправления не только для моих библиотек, но и для наших зависимостей нижестоящего уровня. Это значительные экономические затраты для нас, которые не создают новых улучшений производительности для наших пользователей. Кто-то из MSFT должен уделить этому внимание при обсуждении затрат / выгод при внесении этого изменения.

Изменить: вроде, я уже понял технические достоинства - да, спасибо. Пожалуйста, помогите продать неочевидные экономические выгоды от внесения этого изменения по сравнению с затратами. Зачем пользователям в любом случае делать рывок и переходить на .NET 5, учитывая, что это за «крысиное гнездо»?

@tarekgh как анализатор C # проявляет себя в кодовой базе VB или F #?

Мы можем охватить C # и VB с помощью одного анализатора (мы стремимся сделать наши анализаторы независимыми от языка, где это возможно), но в настоящее время мы не можем получить покрытие анализатора для F #.

@Aaronontheweb, кто использует культурную функциональность и предполагает, что она не изменится, уже сломана, даже если мы не вносили изменений в ICU. Посмотрите блог https://docs.microsoft.com/en-us/archive/blogs/shawnste/locale-culture-data-churn от команды Windows, в которой говорится, что даже поведение NLS меняется в угоду улучшениям. Итак, проблема здесь не в переходе в ICU, а в том, чтобы просто уловить какие-либо неправильные предположения с точки зрения приложений / библиотек. обновление до 5.0 такое же, как обновление до других предыдущих версий. приложения получат много новых интересных функций, и приложения должны быть протестированы, а также могут быть некоторые критические изменения между выпусками. Я не считаю, что изменение поведения глобализации действительно ломает, поскольку мы всегда говорим, что глобализация может измениться в любое время между версиями ОС и версиями ОС. Как указывалось ранее, у нас есть переключатель конфигурации, чтобы выбрать обновление до 5.0 с продолжением использования NLS. из-за этого ICU на самом деле не является фактором при принятии решения о модернизации. теперь с ICU у приложений будет возможность добиться большей согласованности между ОС и даже будет больше контроля над поведением глобализации, если будет принято решение использовать ICU локального приложения. Мы предоставляем приложениям гораздо больший контроль, чем раньше.

Связанный: https://github.com/dotnet/runtime/issues/43802

(Эта проблема не отслеживает IndexOf _per se_. Скорее, в ней обсуждаются непредвиденные последствия выполнения процедуры Compare умолчанию для компаратора с учетом языка и региональных параметров.)

Чем может помочь любой анализатор для существующих проектов?

Анализаторы сканируют код и обнаруживают вызовы API-интерфейсов сопоставления, которые не передают флаги, могут помочь просмотреть такие вызовы и исправить, если обнаружена проблема.

Думаю, в csproj.
Это не произойдет автоматически. Таким образом, многие существующие проекты будут перенесены на .NET 5 без этих анализаторов.
Плюс, как уже упоминалось, это не поможет для проектов F #.

@jeffhandley, спасибо, это подтверждает то, что я уже понял. Поэтому важно признать, что возможное «обходное решение» не поможет пользователям F # (рынок небольшой, но, тем не менее, полностью поддерживается MS как первоклассным гражданином .NET). Я без понятия что это значит:

Изменение происходит внутри среды выполнения .NET, которая должна быть глобальной и не ограничиваться C #.

Я без понятия что это значит:
Изменение происходит внутри среды выполнения .NET, которая должна быть глобальной и не ограничиваться C #.

Я имел в виду любой язык, использующий среду выполнения .NET, а не только C #.

Я собираюсь еще раз решительно высказать свое мнение о том, что из коробки должен быть анализатор, чтобы выявить эти подводные камни в новом мире, особенно если планируется изменить текущее поведение.

Я полностью понимаю технические преимущества этого и не предлагаю вам не вносить изменения (в долгосрочной перспективе это кажется правильным). Я также не говорю, что это еще не было задокументировано как лучшая практика. Я говорю о том, что нам действительно нужно, чтобы это была большая красная мигающая ошибка для разработчиков, пытающихся перейти на .NET 5. Изначально разработчики предполагают, что в противном случае это «просто работает».

Прямо сейчас вы можете использовать эту библиотеку анализатора Roslyn от @meziantou, чтобы найти затронутые области: https://github.com/meziantou/Meziantou.Analyzer/tree/master/docs.

В данном конкретном случае это вызовет MA0074 - Избегайте неявных методов, чувствительных к культуре.

image

При этом, как говорится, это действительно должно быть из коробки, я открыл эту проблему Roslyn здесь: https://github.com/dotnet/roslyn-analyzers/issues/4367

@tarekgh Спасибо за разъяснения. Я исходил из того, что анализаторы - это не ответ, если вы ищете решение, которое работает для всех пользователей .NET.

Мы ищем другие способы помочь разработчикам быть осознанными при вызове чего-то вроде IndexOf и намеренно предоставлять флаги StringComparison для выражения намерения. Мы также приветствуем любые идеи, которые могут возникнуть у вас.

Как насчет того, чтобы отказаться от старых методов (с использованием атрибута [Obsolete] )?

Что происходит с совместимостью .NET Standard, когда поверхность API остается прежней, но поведение меняется?

Переход на учет кластеров графем меня не беспокоит, если влияние хорошо задокументировано. Изменение, которое приводит к тому, что тесно связанные члены семейства строковых методов ведут себя непоследовательно по отношению друг к другу.

Тот, кто занимается неформальной перестановкой строк, равнодушен к неясности символов, кластеров графем или языкового стандарта, будет считать, что если str.Contains (something) завершится успешно, нет необходимости проверять результат str.IndexOf (что угодно ), потому что нам только что сказали, что он там, и поэтому его можно найти. Не имеет значения, какой второй параметр, о котором они не хотят знать, является значением по умолчанию, потому что значение по умолчанию обязательно будет вести себя одинаково для всех методов , освобождая их от необходимости изучать все тонкости, чтобы вообще их использовать.

Подобные несоответствия создают язык, которым могут успешно пользоваться только эксперты, и отталкивают людей, выходящих из лагеря кода. Не поднимайте таким образом планку входа.

Я согласен с @lupestro. Несоответствие в поведении метода очень настораживает. Если вы собираетесь использовать давние методы, которые будут действовать по-разному и непоследовательно, будет много печали. Люди столкнутся с этим, они будут чувствовать себя преданными API, а затем будут гадать, какие еще бомбы замедленного действия ждут, чтобы взорваться. Многие из тех, кто принял .NET, спишут со счетов C # из-за такого рода проблем. Похоже, вам следует либо удалить перегрузки, которые не принимают языковой стандарт, либо нормализовать языковой стандарт по умолчанию для методов. Уже есть Compare и CompareOrdinal, возможно, потребуется (Last) IndexOf (Any) и (Last) IndexOf (Any) Ordinal. Мне не нравится это решение, но, по крайней мере, оно согласуется с тем, что существует в настоящее время. Возможно, стоит отказаться от использования порядкового номера в строке. Если мне нужно выбирать между быстрым или правильным, я каждый раз буду выбирать «правильно». Непоследовательное, неинтуитивное поведение очень расстраивает.

Я вижу, что эта проблема уже закрыта, поэтому я полагаю, что уже решено, что это будет продолжено в .NET 5.0. Я знаю, что это сложно, и информация о культуре (например, о времени) может меняться по разным нетехническим причинам. Разработчики должны знать об этом, но они также должны полагаться на свои API, чтобы быть самосогласованными. По крайней мере, должно быть предупреждение (в 5.0), указанное @aolszowka, которое указывает на проблему ... и, что более важно, почему это проблема. Двигаться вперед очень важно, и иногда это означает, что вам нужно «ломать» старые модели поведения / предположения. Это не означает, что необходимо вносить новые несоответствия. Это изменение ломает не только код, но и ожидания. Если невозможно сделать методы согласованными, я бы предпочел, чтобы CultureInfo был явным (который я мог бы затем адресовать с помощью метода расширения), чем просто иметь возможность неинтуитивной ошибки, взорвавшей мой код во время напряженный цикл разработки или, что еще хуже, установка у клиента.

TL; DR: измените то, что нужно изменить, но не делайте этого непоследовательным в API. Если вы собираетесь сломать его, замените его чем-нибудь получше.

Я использую .NET в течение многих лет и всегда по умолчанию использую InvariantCulture при использовании этих функций. Что касается языков, отличных от английского, я всегда знал о парах символов, которые работают как псевдонимы для букв, специфичных для других языков, и о дополнительной работе, связанной с проверкой этих пар при сравнении с CurrentCulture по умолчанию. Это укусило меня, например, при написании кода ASP.NET, который устанавливает CurrentCulture потока на основе предпочтительного языка пользователя, отправляемого браузером, и сравнения для пользователя, не использующего английский язык, заставляют код ломаться тонкими способами, особенно когда строки содержат смесь введенного пользователем (лингвистического) и порядкового текста.

Такое поведение кластера графем Unicode для меня в новинку, поскольку я ожидал порядкового поведения для перевода строки и возврата каретки, даже если бы я использовал инвариантные перегрузки, как обычно. Мне кажется, что поведение, которое одновременно выполняет больше работы и требует экспертных знаний, должно быть добровольным, независимо от технической обоснованности. Возможно, этот корабль давно отплыл, но _это критическое изменение_ должно быть сделано так же прозрачно, как, например, изменение языка, без необходимости читать малоизвестный блог. Мои коллеги почти не знают о новых возможностях C # 9.0, не говоря уже о каком-то загадочном правиле ICU, которое может повлиять на код, который они пишут сегодня, который когда-нибудь может быть портирован и скомпилирован в .NET 5 (или, что более вероятно, .NET 6 или 7).

Мы рассматриваем отказ от старых методов. Вчера вечером я закончил черновик предложения, ищу его для внутреннего рассмотрения и опубликую здесь как новый выпуск через несколько часов.

Черновик размещен на https://github.com/dotnet/runtime/issues/43956. Он перечисляет несколько альтернатив возможных путей продвижения вперед (включая бездействие) и взвешивает плюсы и минусы каждого подхода. Пожалуйста, не стесняйтесь оставлять здесь любые отзывы о предлагаемых планах действий.

Если вы сообщаете об ошибке, сообщите о новой проблеме и используйте ее для описания ошибки.

Если у вас есть комментарии по этой конкретной проблеме ( "\r\n" vs. "\n" ), продолжайте отвечать в этой теме. Благодаря!

Повторное открытие, пока мы рассматриваем подходы, описанные в документе @GrabYourPitchforks .

Мы вас слышим - здесь много четких отзывов - мы работаем, чтобы найти правильный путь вперед, и будем держать эту проблему в курсе.

Переход на учет кластеров графем меня не беспокоит, если влияние хорошо задокументировано. Изменение, которое приводит к тому, что тесно связанные члены семейства строковых методов ведут себя непоследовательно по отношению друг к другу.

Тот, кто занимается неформальной перестановкой строк, равнодушен к неясности символов, кластеров графем или языкового стандарта, будет считать, что если str.Contains (something) завершится успешно, нет необходимости проверять результат str.IndexOf (что угодно ), потому что нам только что сказали, что он там, и поэтому его можно найти. Не имеет значения, какой второй параметр, о котором они не хотят знать, является значением по умолчанию, потому что значение по умолчанию обязательно будет вести себя _ одинаково для всех методов_, освобождая их от необходимости изучать все тонкости для их использования.

Подобные несоответствия создают язык, которым могут успешно пользоваться только эксперты, и отталкивают людей, выходящих из лагеря кода. Не поднимайте таким образом планку входа.

Да, это полностью выразило мои опасения. Как типичный китайский разработчик, мы редко указываем StringComparison или CultureInfo явно при вызове методов, связанных со строками, в кодах наших приложений, и это просто работает. Мы определенно не ожидаем различного поведения между IndexOf и Contains !
.net 5.0
image
.net ядро ​​3.1
image
.NET Framework
image

Я согласен с @lupestro. Несоответствие в поведении метода очень настораживает. Если вы собираетесь использовать давние методы, которые будут действовать по-разному и непоследовательно, будет много печали.

Возможно, ключевым моментом здесь является то, что эти два метода всегда были несовместимы. В .NET 5.0 они не стали внезапно противоречивыми. Если я правильно слежу за вещами, IndexOf всегда использовал сравнение текущей культуры, Contains всегда использовал порядковое сравнение. Конечно, .NET 5.0 добавляет больше несогласованности. Но ошибка здесь заключалась в оригинальном дизайне API, допускавшем это несоответствие.

Если я правильно слежу за вещами, IndexOf всегда использовал порядковое сравнение, Contains всегда использовало сравнение текущей культуры. Конечно, .NET 5.0 добавляет больше несогласованности. Но ошибка здесь заключалась в оригинальном дизайне API, допускавшем это несоответствие.

Это правильно, но все наоборот: IndexOf(string) использует текущую культуру, IndexOf(char) использует порядковый номер, а Contains использует порядковый номер.

Я кратко расскажу о различиях между IndexOf и Contains , на которые недавно ссылались другие.

IndexOf(string) всегда предполагал сравнение _CurrentCulture_, а Contains(string) всегда предполагал сравнение _Ordinal_. Это несоответствие существовало еще в .NET Framework. Это не новое несоответствие, появившееся в .NET 5. Например, в .NET Framework (который использует средство NLS Windows) лигатура «æ» и двухсимвольная строка «ae» сравниваются как равные при лингвистическом компараторе. Это приводит к следующему несоответствию:

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

Это несоответствие существует уже более десяти лет. Это задокументировано, и на этот счет есть руководство . Это неправильный дизайн? Возможно. Мы обсуждаем на https://github.com/dotnet/runtime/issues/43956 способы сделать экосистему более здоровой и продвигаться вперед.

Что касается этой конкретной проблемы, мы действительно спрашиваем себя: «Учитывая, что несоответствие существовало вечно, насколько далеко друг от друга эти два метода могут отклоняться _ на практике_, прежде чем они нанесут вред более широкой экосистеме?» Я думаю, мы все еще пытаемся определить, где должен быть этот порог. Подобные отчеты _ чрезвычайно_ полезны, потому что они дают нам представление о людях, использующих эти API на практике, и о том, чего ожидают клиенты от их поведения. Как распорядители фреймворка, мы должны учитывать не только техническую документацию, но и то, как реальные приложения используют эти API.

Этот комментарий на самом деле не предназначен для защиты какой-либо конкретной точки зрения. Я намерен прояснить некоторые заблуждения, которые я видел, и помочь объяснить, как мы сформулировали проблему.

Даже если указано InvariantCulture, правильно ли поведение \n не совпадает в версии ICU?

image
image

Может быть, следующий код отображает 5 в Linux и -1 в Windows, если мы используем git и его настройки по умолчанию (autocrlf = true)?

using System;

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

@ufcpp Да, для каждого ICU это ожидаемое поведение, как обсуждалось ранее в этом потоке. Два символа <CR><LF> когда они встречаются рядом друг с другом, считаются неразрывной единицей для лингвистических целей. Поиск <LF> с помощью лингвистического компаратора (такого как _InvariantCulture_) не даст совпадения, поскольку он разделит эту неразрывную единицу.

Я хотел бы поделиться обновлением со всеми: мы решили оставить ICU по умолчанию для 5.0 GA. Мы рассмотрим устранение ловушки случайного использования лингвистического сравнения, когда был задан порядковый номер, который отслеживается в https://github.com/dotnet/runtime/issues/43956. Мы также обратимся к некоторым библиотекам, которые, по нашему мнению, затронуты этой проблемой. В ближайшие дни мы поделимся другими документами, которые будут сопровождать предстоящий выпуск 5.0, которые помогут людям лучше идентифицировать проблемный код и исправить его, чтобы избежать этой проблемы.

Это было очень сложное решение, и нам пришлось взвесить влияние совместимости на экосистему с стремлением к стандартизации между платформами.

Мы оставим эту проблему открытой, чтобы рассмотреть дальнейшие меры по устранению проблемы \r\n при обслуживании, если будет определено, что текущие меры по снижению риска недостаточны.

Я также хотел бы призвать людей продолжать сообщать о проблемах, которые возникают в результате переключения ICU, даже если поведение может иметь известную причину, это не означает, что это «намеренно». Мы продолжим исследовать различия, понять основную причину и определить, нужно ли нам вносить изменения в ICU или .NET для их устранения.

Есть правило анализатора, по которому всегда указывается StringComparison.

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

Я хотел бы поделиться обновлением со всеми: мы решили оставить ICU по умолчанию для 5.0 GA. Мы рассмотрим устранение ловушки случайного использования лингвистического сравнения при назначении порядкового номера, это отслеживается в # 43956. Мы также обратимся к некоторым библиотекам, которые, по нашему мнению, затронуты этой проблемой. В ближайшие дни мы поделимся другими документами, которые будут сопровождать предстоящий выпуск 5.0, которые помогут людям лучше идентифицировать проблемный код и исправить его, чтобы избежать этой проблемы.

Это было очень сложное решение, и нам пришлось взвесить влияние совместимости на экосистему с стремлением к стандартизации между платформами.

Мы оставим эту проблему открытой, чтобы рассмотреть дальнейшие меры по устранению проблемы \r\n при обслуживании, если будет определено, что текущие меры по снижению риска недостаточны.

Я также хотел бы призвать людей продолжать сообщать о проблемах, которые возникают в результате переключения ICU, даже если поведение может иметь известную причину, это не означает, что это «намеренно». Мы продолжим исследовать различия, понять основную причину и определить, нужно ли нам вносить изменения в ICU или .NET для их устранения.

Я хотел бы знать, могу ли я по-прежнему использовать зависимости .NET Standard, которые я не контролирую.

Подозреваю, что не смогу. Как насчет того, чтобы люди переходили, я не знаю, на .NET Standard 2.2? Сменить API?

Чтобы я мог точно знать, что получаю ожидаемое поведение.

Я не уверен, как еще мы можем избежать разрушения существующей экосистемы, которая уже имеет свою долю проблем.

Я был бы рад, что ошибаюсь, пожалуйста, делайте это с предубеждением - так мне будет легче :)

@rcollina

Я хотел бы знать, могу ли я по-прежнему использовать зависимости .NET Standard, которые я не контролирую.

Стандартные библиотеки .Net обычно должны быть кроссплатформенными. И если какая-то библиотека сегодня правильно работает в .Net Core 3 в Unix (которая использует ICU), то она почти наверняка также будет работать в .Net 5 в Windows (которая также использует ICU).

.NET Standard 2.2

.Net Standard vNext фактически существует, хотя и называется ".Net 5.0". (То есть, если вы пишете библиотеку и не заботитесь о поддержке старых фреймворков, сегодня вы бы нацелились на .Net Standard 2.1. Через месяц вместо этого вы бы нацелились на .Net 5.0.)

@svick Понятно . Думаю, я уже понимаю, как работает .NET Standard. Я понимаю, что .NET 5 - это как бы новый .NET Standard.

Приношу свои извинения, но я все еще не уверен, что происходит, когда я ссылаюсь на библиотеку .NET Standard 2.1, которая полагалась на ранее существовавшее несоответствие поведения между IndexOf и Contains.

Что здесь меняется, так это что-то внеполосное, ICU по сравнению с NLS. Это изменение увеличивает расхождение, которое у нас уже было, и разрушает ожидания.
Эту информацию ничего не кодирует в библиотеки.

Я не предполагаю, что понимаю все последствия, но я не могу избавиться от ощущения, что мы «технически правильны» без плавного перехода к новой норме. Что крайне необходимо.

Как заметил кто-то другой, большинство людей даже не знают о новых языковых функциях, не говоря уже о сейсмических изменениях, подобных этому, случайно обнаруженным нашими лучшими членами сообщества. Каковы шансы, что мы сможем противостоять этому?

Я хотел бы знать, могу ли я по-прежнему использовать зависимости .NET Standard, которые я не контролирую.

Что здесь меняется, так это что-то внеполосное, ICU по сравнению с NLS. Это изменение увеличивает расхождение, которое у нас уже было, и разрушает ожидания.

Нет, это не так. Здесь нельзя не подчеркнуть, что ICU _ всегда_ использовался в Unix. Стандартные библиотеки .NET должны быть портативными по дизайну, и все, что ранее работало в Linux .NET Core 3.x, будет работать в .NET 5.

Большая часть работы, которую мы делаем, - это InvariantCulture, которая, согласно моему предыдущему пониманию, должна быть неизменной по дизайну.

Не правда. Предполагается, что InvariantCulture игнорирует только языковые настройки пользователя. Он все еще может измениться с обновлениями спецификации Unicode или библиотек глобализации и т. Д.

Учитывала ли команда .NET боль, проблемы и расходы, которые это могло вызвать?

Подобные замечания меня бесконечно раздражают. Любой код, который внезапно ломается из-за этого изменения, изначально был неправильным. Как платформа должна продвигаться вперед, если команде .NET необходимо сохранить поведение кода пользователя, которое является неправильным или полагается на детали реализации? Не то чтобы они не предоставили переключатель совместимости. Большая часть причины, по которой .NET Core отделилась от .NET Framework, заключалась в том, чтобы решить эту проблему с помощью таких функций, как параллельная установка и локальное развертывание среды выполнения приложения. Если из-за этого вы не можете перейти на .NET 5, не переходите на .NET 5.

Я просто не в восторге от перспективы того, что .NET 5 представит новый урожай неизвестных неизвестных и пересмотрит эти исправления не только для моих библиотек, но и для наших зависимостей нижестоящего уровня.

Если вы устранили все свои ошибки, связанные с различием платформ, как вы утверждаете, то вам не о чем беспокоиться.

Учитывала ли команда .NET боль, проблемы и расходы, которые это могло вызвать?

Подобные замечания меня бесконечно раздражают.

На .NET работают миллионы проектов, и манипуляции со строками - очень частая операция.
Усилия, которые требуется команде .NET, чтобы исправить / изменить это, ничтожны по сравнению с усилиями, необходимыми для проверки всего существующего кода, который будет перенесен на .NET 5/6.

Так что вполне справедливо спросить о «плане» решения этой проблемы.
Есть какой-нибудь план?
Был ли оценен эффект этого изменения?
Это 0,001% от всех проектов? Это 75%?
О каких еще подобных изменениях мы не знаем?

Возможно, это касается лишь небольшого количества проектов.
Но было ли это оценено?

Кстати, я полностью за нарушение изменений, учитывая достаточно веские причины. Но нам также нужен не слишком рискованный путь миграции.

@petarrepac Не поймите меня неправильно, я это понимаю. Но, как неоднократно отмечалось в этой теме:

  1. Есть план, и он состоит в том, чтобы предоставить переключатель конфигурации времени выполнения.
  2. Поведение, которое, как утверждается, нарушает, является существующим поведением .NET на всех платформах, отличных от Windows.
  3. Это должно влиять только на код, который выполняет операции с учетом языка и региональных параметров там, где был задан порядковый номер.

Учитывая последние два пункта, вероятно, разумно предположить, что это влияет на довольно небольшой процент проектов.

На 100% справедливо спросить об этом, но люди, которые пишут комментарии, подобные приведенному мною, часто просто предполагают, что никакие соображения не были внесены и написаны, прежде чем попытаться понять более широкую картину, стоящую за изменением.

Всем привет. Мы хотели кратко описать действия, которые мы предприняли, когда эта проблема была открыта, и в конце концов, почему мы решили оставить значение по умолчанию для Windows 10 May 2019 Update или более поздней версии как ICU для .NET 5.0.

Когда проблема была открыта, мы начали внутреннее обсуждение возможных последствий и боли, которые это могло бы причинить нашим клиентам, учитывая несоответствие между Contains(string) которое составляет Ordinal и IndexOf(string) учетом языка и региональных параметров, а также наличие других API-интерфейсов, которые по умолчанию поддерживают язык при работе со строкой, но имеют значение Ordinal при работе с Span<char> или ReadOnlySpan<char> . Поэтому после обсуждения этой проблемы мы начали анализировать пакеты NuGet, которые могут быть затронуты, и собрали данные, чтобы получить четкую картину. Это были наши выводы:

  • «\ n» занимает 30-е место среди «наиболее часто используемых строковых литералов», передаваемых в IndexOf, LastIndexOf, EndsWith, StartsWith, Compare и CompareTo.
  • 1% сайтов для вызова String.EndsWith имеют строку поиска, начинающуюся с \ n. Любой из этих call-сайтов, где проверяемая строка содержит «окончания строк в стиле Windows», будет поврежден.
  • На NuGet.org размещено 2040 идентификаторов пакетов, версия которых содержит сборку с уязвимым сайтом вызова.
  • Подавляющее большинство этих call-сайтов относятся к EndsWith и IndexOf, что согласуется с гипотезой о том, что этот шаблон часто используется для наивных алгоритмов разрыва строки.

Из этих 2040 пакетов, у которых был опасный сайт вызова, только 539 поддерживают некоторую версию .NET Standard или .NET Core, а это означает, что только 0,54% пакетов, перечисленных на NuGet.org, вероятно, будут подвержены взлому.

Мы рассмотрели пакеты в списке из 539 идентификаторов потенциально затронутых пакетов, чтобы получить представление о фактическом влиянии на них. Мы просмотрели 70 лучших (по количеству загрузок), 20 не раскрывали шаблон в последней версии, из тех, которые раскрывали шаблон, мы могли посмотреть только 32, у которых была разрешенная лицензия:

  • 14 не были подвержены ошибке
  • 13 потенциально были сломаны
  • 5 были неопределенными (не было четких указаний на разрывы из-за защитного кодирования для различных шаблонов разрыва строки или других смягчений).

Это означает, что из 70 лучших пакетов по загрузке только 18% были потенциально затронуты.

Эти проценты суммируются, а не от общего количества пакетов на NuGet.org, которое составляет 229 536. Итак, если бы мы использовали общее количество пакетов и общее количество загрузок в NuGet.org, мы бы увидели 539 потенциально затронутых пакетов из 229 536, что составляет 0,24%.

И хотя нам хорошо анализировать библиотеки, nuget представляет лишь небольшую часть кода C #. И даже если кому-то принадлежит код, а) ошибки может быть нелегко отследить и б) у них может больше не быть исходного кода.

Однако это был хороший источник данных, чтобы сделать вывод о том, что, хотя это могло быть очень заметным изменением в поведении, это перерыв, который уже произошел в Unix при чтении входных данных, которые могут содержать окончания строк Windows (что может быть не так часто).

В .NET Core и .NET 5+ мы стремимся к согласованности между ОС, и, учитывая влияние этого изменения, это казалось правильным решением. Мы действительно заботимся о совместимости и, следовательно, обеспечиваем переключение времени выполнения, чтобы люди могли вернуться к устаревшему поведению.

Кроме того, вывод из пакетов, который мы могли проверить, учитывая, что поведение уже отличается в Unix, мы действительно видели много защитных программ против этой проблемы, чтобы смягчить возможные перерывы в работе ОС.

В дополнение к этому, глобализация может измениться в любое время, поскольку мы являемся тонкой оболочкой для всей ОС, поэтому в данный момент казалось правильным использовать одну и ту же оболочку для всех поддерживаемых нами ОС.

В рамках этого мы улучшили нашу документацию практическими примерами, правилами анализатора roslyn и тем, как затронутый код может смягчить это.

Благодарим вас за все ваши ценные отзывы, поскольку они всегда приводят нас к лучшему, и мы постараемся продолжать улучшать этот опыт для .NET 6, как обсуждается на: https://github.com/dotnet/runtime/issues/43956

Поскольку мы понимаем боль, которую это может вызвать из-за различий между окончаниями строк в Unix и Windows, мы оставляем эту проблему открытой и исследуем возможный способ смягчить проблему \r\n для .NET 5.0. .x, который должен быть частью служебного выпуска.

Также есть разница с char и string:

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

@mattleibow при использовании поиска по символам выполняет поиск по порядковому https://docs.microsoft.com/en-us/dotnet/api/system.string.indexof?view=net-5.0#System_String_IndexOf_System_Char_, в котором указано This method performs an ordinal (culture-insensitive) search . Если вы выполните поиск по строке с порядковым номером, вы получите тот же результат, что и символ.

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

~ Кажется, что CA1307 срабатывает только по indexof(char) но не indexof(string) . Есть ли правило для версии string ? Это кажется более важным, поскольку по умолчанию char автоматически использует порядковый номер, и критическое изменение на самом деле не влияет на это, в то время как поведение string изменилось, и вам необходимо указать порядковый номер для восстановления поведения в некоторые случаи. ~

~ Как определить indexof(string) ? ~

Нашел, это правило CA1310. Наши документы неверны для https://docs.microsoft.com/en-us/dotnet/standard/globalization-localization/globalization-icu#use -nls-вместо-of-icu и не упоминают этот конкретный вариант. Я обновлю эти документы.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги