web-dev-qa-db-fra.com

Accès insensible à la casse pour dictionnaire générique

J'ai une application qui utilise des dll gérées. Une de ces DLL retourne un dictionnaire générique:

Dictionary<string, int> MyDictionary;  

Le dictionnaire contient des touches avec majuscules et minuscules.

D'un autre côté, je reçois une liste de clés potentielles (chaîne), mais je ne peux pas garantir le cas. J'essaie d'obtenir la valeur dans le dictionnaire en utilisant les clés. Mais bien sûr, ce qui suit va échouer car j'ai une incompatibilité de cas:

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

J'espérais que TryGetValue aurait un indicateur ignorer la casse comme indiqué dans le MSDN doc , mais il semble que ce ne soit pas valide pour les dictionnaires génériques.

Existe-t-il un moyen d’obtenir la valeur de ce dictionnaire en ignorant le cas clé? Existe-t-il une meilleure solution de contournement que de créer une nouvelle copie du dictionnaire avec le paramètre approprié StringComparer.OrdinalIgnoreCase ?

208
TocToc

Il n'y a aucun moyen de spécifier un StringComparer au point où vous essayez d'obtenir une valeur. Si vous y réfléchissez, "foo".GetHashCode() et "FOO".GetHashCode() sont totalement différents, il n'y a donc aucun moyen raisonnable de mettre en œuvre une récupération insensible à la casse sur une carte de hachage sensible à la casse.

Vous pouvez toutefois créer un dictionnaire insensible à la casse en utilisant: -

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

Ou créez un nouveau dictionnaire ne respectant pas la casse avec le contenu d'un dictionnaire existant sensible à la casse (si vous êtes sûr qu'il n'y a pas de casse): -

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

Ce nouveau dictionnaire utilise ensuite l'implémentation GetHashCode() sur StringComparer.OrdinalIgnoreCase alors comparer.GetHashCode("foo") et comparer.GetHashcode("FOO") vous donnent la même valeur.

Sinon, si le dictionnaire ne contient que quelques éléments et/ou s'il vous suffit de rechercher une ou deux fois, vous pouvez traiter le dictionnaire d'origine comme un IEnumerable<KeyValuePair<TKey, TValue>> et simplement le parcourir: -

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

Ou si vous préférez, sans le 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;
  }
}

Cela vous évite d'avoir à créer une nouvelle structure de données, mais en contrepartie, le coût d'une recherche est de O(n) au lieu de O (1).

439
Iain Galloway

Pour les LINQers qui n’utilisent jamais un constructeur de dictionnaire classique:

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

Ce n'est pas très élégant, mais au cas où vous ne pouvez pas changer la création du dictionnaire, et tout ce dont vous avez besoin, c'est d'un sale bidouillage, que diriez-vous de ceci:

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