为何SortedDictionary添加不同字符串键报错而Dictionary正常?
问题原因分析
这背后的核心是Unicode字符的两种表示形式差异,以及.NET中Dictionary和SortedDictionary默认采用的键比较规则不同导致的:
1. 两个字符串的本质差异
你看到的两个字符串视觉上完全一致,但它们的Unicode编码其实不一样:
filename1里的é是拆分字符组合:由字母e(编码101)加上组合重音符´(编码769,U+0301)两个字符组成filename2里的é是预组合字符:单个Unicode字符é(编码233,U+00E9)
2. 两种字典的比较规则差异
Dictionary<string, int>的判断逻辑
Dictionary默认使用EqualityComparer<string>.Default,也就是基于字节序列的序数(Ordinal)比较。它会严格检查字符串的每个字符编码是否完全一致,因为两个字符串的字节序列不同,所以Dictionary认为是两个不同的键,自然可以正常添加。
你可以用下面的代码验证这一点:
// 返回 false,说明序数比较下两个字符串不相等 bool ordinalEqual = string.Equals(filename1, filename2, StringComparison.Ordinal);
SortedDictionary<string, int>的判断逻辑
SortedDictionary默认使用Comparer<string>.Default,也就是基于当前系统文化的排序比较。在绝大多数文化(比如法语、英语文化)中,Unicode的拆分组合字符和预组合字符会被视为语义等价的——简单说就是排序时认为它们是同一个字符。所以当你添加第二个键时,SortedDictionary判定这是重复键,抛出ArgumentException。
同样可以用代码验证:
// 返回 true,说明文化敏感比较下两个字符串被视为相等 bool cultureEqual = string.Equals(filename1, filename2, StringComparison.CurrentCulture);
解决方案
如果希望SortedDictionary也像Dictionary那样严格按字符编码判断键是否相等,可以在初始化时指定序数比较器:
// 指定StringComparer.Ordinal,按字节序列严格比较 var files2 = new SortedDictionary<string, int>(StringComparer.Ordinal); files2.Add(filename1, 1); files2.Add(filename2, 2); // 现在可以正常执行,不会抛出异常
内容的提问来源于stack exchange,提问作者A.Baudouin
相关产品推荐
相关产品推荐

