当我在.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的回答包含原因。
尽管破解.csproj
或runtimeconfig.json
文件可能会节省您的时间,但真正的解决方案是明确指定比较:
// this returns the expected result
int idx = s.IndexOf("n", StringComparison.Ordinal);
出于某种原因,IndexOf(string)
默认使用当前区域性比较,当应用在与你的区域设置不同的环境中执行时,即使使用早期的 .NET 版本,也会引起意外。
使用特定于区域性的搜索实际上是一种非常罕见的情况(例如,在浏览器、图书阅读器或 UI 搜索中可能有效),并且它比序号搜索慢得多。
同样的问题也适用于StartsWith
/EndsWith
/Contains
/ToUpper
/ToLower
甚至格式化类型的ToString
和Parse
方法(尤其是在使用浮点类型时),因为这些方法默认情况下也使用当前区域性,这可能是许多陷阱的来源。但是最近的代码分析器(例如。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的答案,因为您只需要担心不精确的字符串操作中的这些细节。