字符串.IndexOf 在 .NET 5.0 中返回不同的值



当我在.NET Core 3.1中运行以下代码时,我得到6作为返回值。

// .NET Core 3.1
string s = "Hellornworld!";
int idx = s.IndexOf("n");
Console.WriteLine(idx);

结果:

6

但是当我在 .NET 5.0 中运行此代码时,我得到的结果不同。为什么会这样?

// .NET 5.0
string s = "Hellornworld!";
int idx = s.IndexOf("n");
Console.WriteLine(idx);

结果:

-1

注释和@Ray的回答包含原因。

尽管破解.csprojruntimeconfig.json文件可能会节省您的时间,但真正的解决方案是明确指定比较:

// this returns the expected result
int idx = s.IndexOf("n", StringComparison.Ordinal);

出于某种原因,IndexOf(string)默认使用当前区域性比较,当应用在与你的区域设置不同的环境中执行时,即使使用早期的 .NET 版本,也会引起意外。

使用特定于区域性的搜索实际上是一种非常罕见的情况(例如,在浏览器、图书阅读器或 UI 搜索中可能有效),并且它比序号搜索慢得多。

同样的问题也适用于StartsWith/EndsWith/Contains/ToUpper/ToLower甚至格式化类型的ToStringParse方法(尤其是在使用浮点类型时),因为这些方法默认情况下也使用当前区域性,这可能是许多陷阱的来源。但是最近的代码分析器(例如。FxCop,ReSharper)可以警告你,如果你不使用特定的比较或文化。建议在产品代码中为这些问题设置高严重性。

您的示例代码与 MSDN 上发布的示例代码完全匹配,该代码还描述了在这些摘录中恢复到旧行为的原因和方式(强调我的):

过去,.NET 全球化 API 在不同的平台上使用不同的基础库。在Unix上,API使用Unicode国际组件(ICU),在Windows上使用本地语言支持(NLS)。[...]行为差异在以下方面很明显

区域
  • 性和区域性数据
  • 字符串大小写
  • 字符串排序和搜索
  • 排序键
  • 字符串规范化
  • 国际化域名 (IDN) 支持
  • Linux 上的时区显示名称

若要恢复使用 NLS[与 Windows 10 2019 年 5 月更新及更新版本相关,现在默认使用ICU],开发人员可以选择退出 ICU 实现。应用程序可以通过以下任一方式启用 NLS 模式:

  • 在项目文件中:

    <ItemGroup>
    <RuntimeHostConfigurationOption Include="System.Globalization.UseNls" Value="true" />
    </ItemGroup>
    
  • runtimeconfig.json文件中:

    {
    "runtimeOptions": {
    "configProperties": {
    "System.Globalization.AppLocalIcu": "<suffix>:<version> or <version>"
    }
    }
    }
    
  • 通过将环境变量DOTNET_SYSTEM_GLOBALIZATION_APPLOCALICU设置为值<suffix>:<version><version>.

    <suffix>:长度少于 36 个字符的可选后缀,遵循公共 ICU 打包约定。构建自定义 ICU 时,可以对其进行自定义以生成库名称和导出的符号名称以包含后缀,例如 libicuucmyapp,其中 myapp 是后缀。

    <version>:有效的 ICU 版本,例如 67.1。此版本用于加载二进制文件并获取导出的符号。

有关更多/最新信息,请参阅上面的 MSDN 链接。

但是,我建议也阅读György Kőszeg的答案,因为您只需要担心不精确的字符串操作中的这些细节。

最新更新