对于通用字典的不区分大小写访问
对于通用字典的不区分大小写访问
我有一个使用托管dll的应用程序。其中一个dll返回一个泛型字典: \n
DictionaryMyDictionary;
\n该字典包含大小写不敏感的键。 \n另一方面,我正在获取一个潜在的键列表(字符串),但无法保证大小写。我试图使用这些键从字典中获取值。但是当然以下代码会失败,因为大小写不匹配: \n
bool Success = MyDictionary.TryGetValue(MyIndex, out TheValue);
\n我希望TryGetValue有一个忽略大小写的标志,就像MSDN文档中提到的那样,但是似乎这对于泛型字典无效。 \n有没有办法在忽略键的大小写的情况下获取该字典的值? \n除了使用正确的StringComparer.OrdinalIgnoreCase参数创建字典的新副本之外,有没有更好的解决方法?
问题原因:需要实现对于键的大小写不敏感的访问,但是在C#中,Dictionary类默认是对键的大小写敏感的。
解决方法:可以通过使用StringComparer.OrdinalIgnoreCase来创建一个字典,并将其作为参数传递给Dictionary类的构造函数,从而实现对键的大小写不敏感的访问。
以下是具体的实现代码:
using System; using System.Collections.Generic; var caseInsensitiveDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase);
在这个解决方法中,我们创建了一个新的字典caseInsensitiveDictionary,并使用StringComparer.OrdinalIgnoreCase作为参数传递给它的构造函数。这样,我们就实现了对键的大小写不敏感的访问。
需要注意的是,这种解决方法对于值的类型并不重要,只要键是字符串类型,就可以实现对键的大小写不敏感的访问。
作为一个经常使用有序字典且键为字符串类型的人来说,为什么这个解决方法没有得到更多的赞同,对我来说是不可理解的...
在LINQ中,如果不使用常规的字典构造函数,可以使用myCollection.ToDictionary(x => x.PartNumber, x => x.PartDescription, StringComparer.OrdinalIgnoreCase)
来创建一个字典,该字典可以进行大小写不敏感的访问。
C#还提供了一个构造函数:Dictionary(IDictionary
,它允许您使用新的比较器有效地重新创建相同的字典。
这意味着您可以使用现有的字典来创建一个新的字典,并指定一个自定义的比较器来实现大小写不敏感的访问。
这是非常有用的,因为有时候我们需要在字典中进行不区分大小写的查找或比较。使用这种方式,我们可以确保不管输入是大写还是小写,都能够正确地获取或比较数据。
通过使用StringComparer.OrdinalIgnoreCase
作为比较器,我们可以确保字典的键是大小写不敏感的,这意味着我们可以使用任何大小写方式来访问字典中的值,而不会因为大小写而导致错误的结果。
通过传递现有的字典和一个自定义的比较器给字典的构造函数,我们可以非常简单地实现大小写不敏感的访问。这使得我们的代码更加灵活和健壮,能够处理各种不同的输入情况。
因此,通过使用Dictionary(IDictionary
构造函数,我们可以轻松地实现对字典的大小写不敏感的访问,并确保代码的健壮性和灵活性。
在尝试获取一个值的地方,没有办法指定StringComparer
。如果你仔细思考一下,"foo".GetHashCode()
和"FOO".GetHashCode()
是完全不同的,所以在区分大小写的哈希映射中,你无法合理地实现不区分大小写的获取。
然而,你可以在第一次创建字典时使用不区分大小写的方式来创建一个不区分大小写的字典,如下所示:
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);
这个新的字典使用StringComparer.OrdinalIgnoreCase
上的GetHashCode()
实现,所以comparer.GetHashCode("foo")
和comparer.GetHashcode("FOO")
会给出相同的值。
或者,如果字典中只有几个元素,并且/或者你只需要查找一两次,你可以将原始字典视为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)。
没有理由保留旧字典并实例化新字典,因为任何大小写冲突都会导致它崩溃。如果你知道不会发生冲突,那么最好从一开始就使用不区分大小写。
我已经使用.NET十年了,现在才弄清楚这一点!为什么要使用Ordinal而不是CurrentCulture?
嗯,这取决于你想要的行为。如果用户通过UI提供键(或者如果你需要考虑ss和ß相等),那么你需要使用不同的文化,但是考虑到该值作为哈希映射的键来自外部依赖项,我认为'OrdinalCulture'是一个合理的假设。
虽然这些字典经常由其他东西返回。但是,知道你处理的东西中不会发生大小写冲突,并不能让现有系统以不区分大小写的方式创建它们的字典。
"然而,你可以在第一次创建字典时使用不区分大小写的方式来创建一个不区分大小写的字典。"但是如果字典是接口的一部分呢?有没有办法强制这个属性?
你是说有没有办法定义接口的属性,这样实现该接口的实现类就必须提供不区分大小写的实现?如果是这样,请提一个新的问题,并在这里链接过去。
default(KeyValuePair<T, U>)
不是null
——它是一个KeyValuePair
,其中Key=default(T)
,Value=default(U)
。所以你不能在LINQ示例中使用?.
操作符;你需要先获取FirstOrDefault()
,然后(对于这种特殊情况)检查Key == null
。