Нечувствительный к регистру словарь со строковым ключом в C #

156

Если у меня Dictionary<String,...>есть, можно ли сделать такие методы, как ContainsKeyбез учета регистра?

Это казалось связанным, но я не понял его правильно: c # Dictionary: создание ключа без учета регистра с помощью объявлений

Мистер Бой
источник
6
Что не так с использованием StringComparer.InvariantCultureIgnoreCase? Он делает то, что говорит ...
Леппи
5
Я никогда не слышал об этом, поэтому вопрос!
Мистер Бой
Этот вопрос связан, но не совсем дублирует этот. Это входит в то, как иметь дело с существующим словарем. Я начинаю с нового кода, поэтому ответы здесь лучше подходят.
Goodeye

Ответы:

322

Это казалось связанным, но я не понял его правильно: c # Dictionary: создание ключа без учета регистра с помощью объявлений

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

var dict = new Dictionary<string, YourClass>(
        StringComparer.InvariantCultureIgnoreCase);

Конструктор ожидает, IEqualityComparerчто словарь говорит, как сравнивать ключи.

StringComparer.InvariantCultureIgnoreCaseдает вам IEqualityComparerэкземпляр, который сравнивает строки без учета регистра.

Конрад Рудольф
источник
1
Отлично, я как-то пропустил STL-подобный способ передачи компаратора в ctor.
Мистер Бой
7
Меня не перестает удивлять, как я всегда нахожу здесь ответ на любой вопрос кодирования! Может быть, я назову мою следующую кошку в честь тебя, Конрад! :-)
Джейми
10
Возможно, сравнение StringComparison.OrdinalIgnoreCase быстрее, чем на основе культуры: stackoverflow.com/questions/492799/…
Манушин Игорь
Как мне это сделать, если я не создаю словарь, но пытаюсь использовать ContainsKey () для нечувствительного к регистру совпадения ключей в существующем словаре, который я получаю как часть объекта?
Шридхар Р Кулкарни
1
@ShridharRKulkarni Вы принципиально не можете (эффективно). Логика сравнения является основной частью внутренней структуры данных словаря. Для этого контейнер должен поддерживать несколько индексов для своих данных, а словари этого не делают.
Конрад Рудольф
21
var myDic = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
myDic.Add("HeLlo", "hi");

if (myDic.ContainsKey("hello"))
    Console.WriteLine(myDic["hello"]);
Стив
источник
1
Фрагмент кода говорит обо всем. Очень полезно. Не уверен, почему этот пост имеет меньше голосов по сравнению с принятым ответом только за опоздание на 3 минуты.
RBT
14

Есть немного шансов, когда ваше дело со словарем, который извлекается из сторонних или внешних DLL. Используя linq

YourDictionary.Any(i => i.KeyName.ToLower().Contains("yourstring")))

Kurkula
источник
3
Это прекрасно работает. Слово предостережения , хотя, если вы измените Anyк SingleOrDefaultвам не получите nullобратно , если он не существует, вместо этого вы получите KeyValuePair как с ключом и устанавливающим null!
NibblyPig
1
ContainsПохоже, это очень специфический вариант использования того, над чем вы работали в то время. Как более общий полезный ответ, я думаю, что Equalsлучше. И на этой же ноте вместо дублирования строки ToLower()было бы лучше использовать StringComparison.xxxCase.
Суамер
1
это было очень полезно, и избавление от ToLower, безусловно, является улучшением - мой пример использования: return AppliedBuffs.Any (b => b.Key.Equals (Name, StringComparison.OrdinalIgnoreCase)); идеальный регистронезависимый Содержит регистрозависимый словарь.
Дэвид Берфорд
Я так искушаю понизить это. Слово предостережения, если вы являетесь владельцем словаря, это, безусловно, не способ сделать это. Используйте этот подход, только если вы не знаете, как создавался словарь. Даже в этом случае для точного соответствия строки (а не только для совпадения dict.Keys.Contains("bla", appropriate comparer)деталей ) используйте перегрузку LINQ для простоты использования.
Навфал
1

Я просто столкнулся с такой же проблемой, когда мне понадобился регистр символов в контроллере ASP.NET Core.

Я написал метод расширения, который делает свое дело. Может быть, это может быть полезно и для других ...

public static IDictionary<string, TValue> ConvertToCaseInSensitive<TValue>(this IDictionary<string, TValue> dictionary)
{
    var resultDictionary = new Dictionary<string, TValue>(StringComparer.InvariantCultureIgnoreCase);
    foreach (var (key, value) in dictionary)
    {
        resultDictionary.Add(key, value);
    }

    dictionary = resultDictionary;
    return dictionary;
}

Чтобы использовать метод расширения:

myDictionary.ConvertToCaseInSensitive();

Затем получите значение из словаря с помощью:

myDictionary.ContainsKey("TheKeyWhichIsNotCaseSensitiveAnymore!");
Паром Jongmans
источник
0

Если у вас нет контроля при создании экземпляра, скажем, ваш объект удален от json и т. Д., Вы можете создать класс-оболочку, который наследуется от словарного класса.

public class CaseInSensitiveDictionary<TValue> : Dictionary<string, TValue>
{
    public CaseInSensitiveDictionary() : base(StringComparer.OrdinalIgnoreCase){}
}
AG
источник