关于土耳其语 İ 问题以及你为何应该关注

2012年7月5日 code suggest edit

请看以下代码:

const string input = "interesting";
bool comparison = input.ToUpper() == "INTERESTING";
Console.WriteLine("These things are equal: " + comparison);
Console.ReadLine();

假设 input 实际上是用户输入或从 API 获取的值。 这段代码应该会输出 These things are equal: True,对吧? 没错吧?!

但是,如果你住在土耳其,情况就不是这样了。 更准确地说,如果你的操作系统的当前区域性是 tr-TR (如果你住在土耳其,很可能是这种情况),情况就不是这样了。

为了验证这一点,让我们强制该应用程序使用土耳其语区域设置运行。 以下是执行此操作的控制台应用程序的完整源代码:

using System;
using System.Globalization;
using System.Threading;
internal class Program
{
  private static void Main(string[] args)
  {   
    Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");
    const string input = "interesting";
    
    bool comparison = input.ToUpper() == "INTERESTING";
    Console.WriteLine("These things are equal: " + comparison);
    Console.ReadLine();
  }
}

现在我们看到它输出 These things are equal: False

为了理解为什么会这样,我建议阅读关于这个主题的更详细的文章:

简而言之,英语中 i 的大写形式是 I(注意没有点),但在土耳其语中,它是有点的 İ。 因此,我们有两种 i(大写和小写),而他们有四种。

我的应用程序只支持英语。AMURRICA!

即使你没有计划将你的应用程序翻译成其他语言,你的应用程序也可能会受到这个问题的影响。 毕竟,我发布的示例也只支持英语。

也许不会有很多土耳其人使用你的应用程序,但为什么要让使用它的那些人遇到很容易预防的错误呢? 如果你不注意这一点,很容易导致代价高昂的安全漏洞。

解决方案很简单。 在大多数情况下,当你比较字符串时,你希望使用 StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase 进行比较。 事实证明,比较字符串的方法有很多。 不仅仅是 String.Equals

代码分析来救援

我一直是 FxCop 的粉丝。 有时它似乎是一个唠叨的保姆,不断警告你那些你不在乎的垃圾。 但在所有这些警告中,隐藏着一些重要的规则,可以防止一些愚蠢的错误。

如果你有幸从头开始一个在 Visual Studio 2010 或更高版本的项目中,我强烈建议启用 Code Analysis(FxCop 已集成到 Visual Studio 中,现在称为 Code Analysis)。 我的建议是选择一组你关心的规则,并确保如果任何规则被违反,构建就会中断。 不要将它们作为警告打开,因为警告是毫无意义的噪音。 如果它不够重要以至于不能中断构建,那么它就不够重要以至于不能添加它。

当然,我们中的许多人都在处理从一开始就没有强制执行这些规则的现有代码库。 在事后添加代码分析是一项艰巨的任务。 这是我最近采取的一种方法,它帮助我保持了理智。 至少是剩下的部分。

首先,我手动创建了一个包含以下内容的文件:

<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="PickAName" Description="Important Rules" ToolsVersion="10.0">
 <Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis"
   RuleNamespace="Microsoft.Rules.Managed">
  <Rule Id="CA1309" Action="Error" />  
 
 </Rules>
</RuleSet>

你可以为每个项目创建一个,但我决定为我的解决方案创建一个。 维护多个规则集只是一种痛苦。 我将此文件命名为 SolutionName.ruleset 并将其放在我的解决方案的根目录中(名称无关紧要。只需将扩展名设为 .ruleset)。

然后,我配置了我解决方案中我关心的每个项目(我忽略了单元测试项目)以使用此规则集文件启用代码分析。 只需转到项目属性并选择 Code Analysis 选项卡。

CodeAnalysisRuleSetCodeAnalysisRuleSet

我将所选的 Configuration 更改为 “All Configurations”。 我还选中了 “Enable Code Analysis…” 复选框。 然后我单击 “Open” 并选择我的规则集文件。

此时,每次我构建时,Code Analysis 只会在我构建时运行一条规则,即 CA1309。 这样,添加更多规则就会变得可管理。 每次我修复一个警告时,我都会将该警告一次一个地添加到此文件中。 我浏览了以下列表以寻找重要规则。

我没有添加这些列表中的每个规则,只添加了我认为重要的规则。

在某个时候,我包含了大量的规则,因此我可以反转列表,而不是列出所有我想包含的规则,我只列出我想排除的规则。

<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="PickAName" Description="Important Rules" ToolsVersion="10.0">
 <IncludeAll Action="Error" />
 <Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis"
   RuleNamespace="Microsoft.Rules.Managed">
  <Rule Id="CA1704" Action="None" />  
 
 </Rules>
</RuleSet>

请注意,IncludeAll 元素现在将每个代码分析警告转换为错误,但我随后在列表中关闭了 CA1704。

请注意,你不必手动编辑此文件。 如果你在 Visual Studio 中打开规则集,它将提供一个 GUI 编辑器。 我更喜欢简单地编辑文件。

RuleSetEditorRuleSetEditor

我做的另一件事是,对于一些非常重要的规则,修复这些规则的问题需要很长时间,我会简单地使用 Visual Studio 来抑制所有这些规则并提交该规则。 至少这可以确保不会提交任何新的违反该规则的行为。 这让我可以随意修复现有的规则。

我发现这种方法比简单地打开每个规则并抱最好的希望更有用,也更不痛苦。 希望你觉得这对你也有帮助。 愿你永远不要再次发布包含土耳其语 I 问题的错误!

Found a typo or mistake in the post? suggest edit