通用字典不区分大小写的访问


242

我有一个使用托管dll的应用程序。这些dll之一返回通用字典:

Dictionary<string, int> MyDictionary;  

字典包含大小写键。

另一方面,我得到了潜在键(字符串)的列表,但是我不能保证这种情况。我试图使用键在字典中获取值。但是当然,由于我的案例不匹配,因此以下操作将失败:

bool Success = MyDictionary.TryGetValue( MyIndex, out TheValue );  

我希望TryGetValue具有Mcase doc中提到的忽略大小写标志,但这似乎对通用词典无效。

有没有一种方法可以忽略字典大小写而获得该词典的值?是否有比使用适当的StringComparer.OrdinalIgnoreCase参数创建字典的新副本更好的解决方法?


Answers:


512

StringComparer尝试获取值时,无法指定a 。如果考虑到这一点,"foo".GetHashCode()并且"FOO".GetHashCode()完全不同,那么就没有合理的方法可以在区分大小写的哈希映射上实现不区分大小写的get。

但是,您可以首先使用以下命令创建不区分大小写的字典:

var comparer = StringComparer.OrdinalIgnoreCase;
var caseInsensitiveDictionary = new Dictionary<string, int>(comparer);

或使用现有的区分大小写的字典的内容创建一个新的不区分大小写的字典(如果您确定没有大小写冲突):

var oldDictionary = ...;
var comparer = StringComparer.OrdinalIgnoreCase;
var newDictionary = new Dictionary<string, int>(oldDictionary, comparer);

然后,此新词典使用so 的GetHashCode()实现,并为您提供相同的值。StringComparer.OrdinalIgnoreCasecomparer.GetHashCode("foo")comparer.GetHashcode("FOO")

或者,如果字典中只有几个元素,并且/或者您只需要查找一次或两次,则可以将原始字典视为an IEnumerable<KeyValuePair<TKey, TValue>>并对其进行迭代:-

var myKey = ...;
var myDictionary = ...;
var comparer = StringComparer.OrdinalIgnoreCase;
var value = myDictionary.FirstOrDefault(x => String.Equals(x.Key, myKey, comparer)).Value;

或者,如果您愿意,也可以不使用LINQ:

var myKey = ...;
var myDictionary = ...;
var comparer = StringComparer.OrdinalIgnoreCase;
int? value;
foreach (var element in myDictionary)
{
  if (String.Equals(element.Key, myKey, comparer))
  {
    value = element.Value;
    break;
  }
}

这样可以节省创建新数据结构的成本,但作为回报,查找的成本为O(n)而不是O(1)。


确实有道理。非常感谢您的解释。
TocToc 2012年

1
没有理由保留旧字典并实例化新字典,因为任何大小写冲突都会导致其爆炸。如果您知道不会发生冲突,那么您最好一开始就不区分大小写。
Rhys Bevilaqua

2
我一直在使用.NET已有十年了,现在我才明白了这一点!!为什么使用Ordinal代替CurrentCulture?
乔丹

好吧,这取决于您想要的行为。如果用户通过用户界面提供密钥(或者如果您需要考虑例如ss和ß相等),则需要使用其他区域性,但是鉴于该值被用作来自外部依赖,我认为“ OrdinalCulture”是一个合理的假设。
伊恩·加洛韦

1
default(KeyValuePair<T, U>)不是null-这是and 的KeyValuePair地方。因此,您不能在LINQ示例中使用运算符。您需要抓取,然后(对于这种特殊情况)检查是否为。Key=default(T)Value=default(U)?.FirstOrDefault()Key == null
阿瑟伯'19

38

对于那些从未使用常规字典构造函数的LINQers:

myCollection.ToDictionary(x => x.PartNumber, x => x.PartDescription, StringComparer.OrdinalIgnoreCase)

8

它不是很优雅,但是如果您无法更改字典的创建,而您所需要的只是一个肮脏的hack,如何处理:

var item = MyDictionary.Where(x => x.Key.ToLower() == MyIndex.ToLower()).FirstOrDefault();
    if (item != null)
    {
        TheValue = item.Value;
    }

13
或仅此:new Dictionary <string,int>(otherDict,StringComparer.CurrentCultureIgnoreCase);
乔丹

6
按照“在.NET Framework中使用字符串的最佳做法”的规定,使用ToUpperInvariant代替ToLowermsdn.microsoft.com/zh-CN/library/dd465121%28v=vs.110%29.aspx
Fred Fred

这对我很有好处,我不得不以一种不敏感的方式回顾性地检查密钥。我将其简化了一些var item = MyDictionary.FirstOrDefault(x => x.Key.ToUpperInvariant() == keyValueToCheck.ToUpperInvariant());
Jay

为什么不只是 dict.Keys.Contains("bla", appropriate comparer)呢?此外,由于C#中的键值对是一个结构,因此FirstOrDefault不会为null。
nawfal
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.