为什么普通的 .Contains() 会让你失望(以及如何修复)
\n\n想象一下:你在用 C# 编写图书目录搜索。用户输入“托尔斯泰”,而数据库中存储的是小写的“托尔斯泰”。普通的 string.Contains("托尔斯泰") 会返回 false。是不是很熟悉?这个问题在 Stack Overflow 上获得了数千次浏览——这并非没有原因。在 2025 年,当 .NET 提供了强大的字符串处理工具时,使用旧方法意味着失去用户和性能。
在本文中,我们将介绍在 C# 中使 .Contains() 不区分大小写的 5 种最佳方法。您将了解哪种方法能提供最快速度,哪种方法能正确处理不同语言。让我们开始吧!
问题:为什么 .Contains() 默认区分大小写?
\n\n在 C# 中,string.Contains() 方法默认使用 Ordinal 比较。这意味着字符是按字节进行比较的:'A' (0x41) 不等于 'a' (0x61)。对于英文内容,这通常没问题,但在多语言应用程序(俄语、德语、土耳其语)中,这种方法会破坏搜索逻辑。
例如,在土耳其语中,大写字母 'i' 是 'İ',而不是 'I'。简单的 ToLower() 可能会产生错误结果。因此,直接调用 text.ToLower().Contains(pattern.ToLower()) 是一种不好的做法。让我们看看现代解决方案。
方法 1:StringComparison.OrdinalIgnoreCase——你的新标准
\n\n从 .NET 5 开始(在 .NET 8/9 中运行良好),最快且最安全的方法是使用带有 StringComparison 参数的 Contains 重载:
string text = "战争与和平——列夫·托尔斯泰";\nstring searchTerm = "托尔斯泰";\n\nbool result = text.Contains(searchTerm, StringComparison.OrdinalIgnoreCase);\nConsole.WriteLine(result); // True\n\n为什么这是最佳选择?
\n- \n
- 速度: OrdinalIgnoreCase 在字节级别工作,但忽略大小写。这比 CurrentCultureIgnoreCase 快 2-3 倍。 \n
- 可预测性: 不依赖于用户的文化设置。如果你不希望 "ß"(德语 Eszett)等于 "ss",它就不会。 \n
- 支持: 在所有现代 .NET 版本(Core 2.1+)中可用。 \n
当你需要进行简单的不区分大小写比较,并且不涉及语言特性(排序、变格)时,请使用此方法。
\n\n方法 2:带 StringComparison 的 IndexOf()——如果需要更多控制
\n\n有时你不仅需要检查是否存在,还需要知道匹配的位置。或者你正在使用较旧的 .NET Framework 版本,其中 Contains 不支持 StringComparison。这时 IndexOf() 可以派上用场:
string text = "C#——最好的后端语言";\nstring searchTerm = "最好";\n\nbool result = text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) >= 0;\nConsole.WriteLine(result); // True\n\n// 如果需要获取位置:\nint index = text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);\nConsole.WriteLine(index); // 4(单词开始的位置)\n\n何时使用:
\n- \n
- 在遗留项目(例如 .NET Framework 4.x)中。 \n
- 当需要找到匹配的索引,而不仅仅是检查是否存在时。
- 用于更复杂的场景(例如,跳过字符的搜索)。
重要提示: 如果未找到子字符串,IndexOf 返回 -1。始终检查 >= 0,而不是 > 0。
方法 3:CultureInfo.CurrentCultureIgnoreCase——用于语言上正确的搜索
\n\n如果你的应用程序需要考虑