Нечувствительный к регистру доступ для общего словаря

У меня есть приложение, использующее управляемые DLL. Одна из этих dll возвращает общий словарь:

Dictionary<string, int> MyDictionary;  

Словарь содержит ключи с верхним и нижним регистром.

С другой стороны, я получаю список потенциальных ключей (строка), однако я не могу гарантировать это. Я пытаюсь получить значение в словаре с помощью клавиш. Но, конечно, следующее будет терпеть неудачу, так как у меня есть несоответствие случая:

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

Я надеялся, что TryGetValue будет иметь флаг игнорирования, как указано в документе MSDN, но, похоже, это не подходит для общих словарей,

Есть ли способ получить значение этого словаря, игнорируя ключевой случай? Есть ли лучший способ обхода, чем создание новой копии словаря с соответствующим параметром StringComparer.OrdinalIgnoreCase?

Ответ 1

Нет способа указать 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);

Затем этот новый словарь использует реализацию GetHashCode() в StringComparer.OrdinalIgnoreCase, поэтому 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).

Ответ 2

Для вас, LINQers, которые никогда не используют обычный конструктор словарей:

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

Ответ 3

Это не очень элегантно, но если вы не можете изменить словарь, и все, что вам нужно, это грязный хак, как насчет этого:

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