Проверьте, существует ли ключ в NameValueCollection

144

Есть ли быстрый и простой способ проверить, существует ли ключ в NameValueCollection, не просматривая его?

Ищете что-то вроде Dictionary.ContainsKey () или подобное.

Конечно, есть много способов решить эту проблему. Просто интересно, может ли кто-нибудь помочь мне избавиться от зуда в мозгу.

Мухаммад Юссуф
источник
просто используйте Dictionary, если вы хотите выполнить поиск на основе ключа .... Кстати: вы можете использовать индексатор для этого класса, но это сделает сам цикл - так что никакого выигрыша
Carsten

Ответы:

185

Из MSDN :

Это свойство возвращает значение null в следующих случаях:

1) если указанный ключ не найден;

Итак, вы можете просто:

NameValueCollection collection = ...
string value = collection[key];
if (value == null) // key doesn't exist

2) если указанный ключ найден и связанное с ним значение равно нулю.

collection[key]вызывает base.Get()then, base.FindEntry()который внутренне использует Hashtableс производительностью O (1).

Абатищев
источник
38
Это свойство возвращает null в следующих случаях: 1) если указанный ключ не найден; и 2) если указанный ключ найден и связанное с ним значение равно нулю. Это свойство не делает различия между двумя случаями.
Стив,
1
@Andreas, поэтому лучше хранить пустую строку вместо null
абатищев
@Steve OP ничего не говорит о таком столкновении.
Абатищев
13
Правильно @abatishchev, однако OP говорит: «Проверка наличия ключа». Нулевое значение в качестве ключа не существует. В конце концов, нет ответа без компромисса (без цикла, используйте пустые строки)
Стив
@abatishchev это все равно что сказать 0равный null... sry
Андреас Нидермайр
55

Используйте этот метод:

private static bool ContainsKey(this NameValueCollection collection, string key)
{
    if (collection.Get(key) == null)
    {
        return collection.AllKeys.Contains(key);
    }

    return true;
}

Он наиболее эффективен NameValueCollectionи не зависит от того, содержит ли коллекция nullзначения или нет.

Кирилл Полищук
источник
5
Не забывайте using System.Linq;при использовании этого раствора.
jhauberg
14

Я не думаю, что любой из этих ответов является правильным / оптимальным. NameValueCollection не только не делает различий между нулевыми и пропущенными значениями, но и не чувствителен к регистру в отношении ключей. Таким образом, я думаю, что полное решение будет:

public static bool ContainsKey(this NameValueCollection @this, string key)
{
    return @this.Get(key) != null 
        // I'm using Keys instead of AllKeys because AllKeys, being a mutable array,
        // can get out-of-sync if mutated (it weirdly re-syncs when you modify the collection).
        // I'm also not 100% sure that OrdinalIgnoreCase is the right comparer to use here.
        // The MSDN docs only say that the "default" case-insensitive comparer is used
        // but it could be current culture or invariant culture
        || @this.Keys.Cast<string>().Contains(key, StringComparer.OrdinalIgnoreCase);
}
ЧейзМедальон
источник
Мне нравится это решение. Глядя на источник NameValueCollectionBase, он по умолчанию использует InvariantCultureIgnoreCase, однако это не означает, что другой не передается для использования каким-либо классом, создающим экземпляр NameValueCollection.
lethek
12

Да, вы можете использовать Linq для проверки AllKeysсвойства:

using System.Linq;
...
collection.AllKeys.Contains(key);

Однако Dictionary<string, string[]>для этой цели больше подходит a , возможно, созданный с помощью метода расширения:

public static void Dictionary<string, string[]> ToDictionary(this NameValueCollection collection) 
{
    return collection.Cast<string>().ToDictionary(key => key, key => collection.GetValues(key));
}

var dictionary = collection.ToDictionary();
if (dictionary.ContainsKey(key))
{
   ...
}
Рич О'Келли
источник
1
Это будет перебирать всю коллекцию, которая составляет O (n). В то время как collection[key]внутренне использует HashtableO (1)
абатищев
4
@abatishchev В самом деле, однако, не collection[key]делает различий между отсутствующим ключом и нулевым значением, сохраненным для этого ключа.
Рич О'Келли,
Также вы можете выполнить грязный взлом и получить частное поле Hashtable с помощью Reflection.
Абатищев
Я считаю это довольно глупым решением. Если кто-то использует NameValueCollection, вероятно, по причинам, что словарь не поддерживается, например, наличие нулевого ключа.
Крис Марисич
0

Вы можете использовать Getметод и проверить, nullкак метод вернет, nullесли NameValueCollection не содержит указанный ключ.

См. MSDN .

Кодрин Евгений
источник
Вы должны знать , indexиз keyвызвать метод. Не так ли?
Абатищев
0

Если размер коллекции невелик, вы можете воспользоваться решением, предоставленным rich.okelly. Однако большая коллекция означает, что создание словаря может быть заметно медленнее, чем простой поиск в коллекции ключей.

Кроме того, если ваш сценарий использования ищет ключи в разные моменты времени, где NameValueCollection мог быть изменен, создание словаря каждый раз, опять же, может быть медленнее, чем просто поиск в коллекции ключей.

Ягуар
источник
0

Это также может быть решением без необходимости вводить новый метод:

    item = collection["item"] != null ? collection["item"].ToString() : null;
дырка
источник
0

Как видно из справочных источников, NameValueCollection наследуется от NameObjectCollectionBase .

Итак, вы берете базовый тип, получаете частную хеш-таблицу через отражение и проверяете, содержит ли она определенный ключ.

Чтобы он работал и в Mono, вам необходимо увидеть имя хеш-таблицы в моно, которое вы можете увидеть здесь (m_ItemsContainer), и получить монополе, если исходное поле FieldInfo имеет значение null (mono- время выполнения).

Как это

public static class ParameterExtensions
{

    private static System.Reflection.FieldInfo InitFieldInfo()
    {
        System.Type t = typeof(System.Collections.Specialized.NameObjectCollectionBase);
        System.Reflection.FieldInfo fi = t.GetField("_entriesTable", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

        if(fi == null) // Mono
            fi = t.GetField("m_ItemsContainer", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);

        return fi;
    }

    private static System.Reflection.FieldInfo m_fi = InitFieldInfo();


    public static bool Contains(this System.Collections.Specialized.NameValueCollection nvc, string key)
    {
        //System.Collections.Specialized.NameValueCollection nvc = new System.Collections.Specialized.NameValueCollection();
        //nvc.Add("hello", "world");
        //nvc.Add("test", "case");

        // The Hashtable is case-INsensitive
        System.Collections.Hashtable ent = (System.Collections.Hashtable)m_fi.GetValue(nvc);
        return ent.ContainsKey(key);
    }
}

для сверхчистого неотражающего кода .NET 2.0 вы можете перебирать ключи вместо использования хэш-таблицы, но это медленно.

private static bool ContainsKey(System.Collections.Specialized.NameValueCollection nvc, string key)
{
    foreach (string str in nvc.AllKeys)
    {
        if (System.StringComparer.InvariantCultureIgnoreCase.Equals(str, key))
            return true;
    }

    return false;
}
Стефан Штайгер
источник
0

В VB это:

if not MyNameValueCollection(Key) is Nothing then
.......
end if

В C # должно быть просто:

if (MyNameValueCollection(Key) != null) { }

Не уверен, должно ли это быть nullили ""это должно помочь.

CodeShouldBeEasy
источник
Извините, что это в VB. C # должен быть просто if (MyNameValueCollection (Key)! = Null) {} Не уверен, должно ли оно иметь значение null или "", но это должно помочь.
CodeShouldBeEasy
Я считаю, что правильный синтаксис похож на Dictionaryструктуру данных MyNameValueCollection[Key], а не на MyNameValueCollection(Key), которая ожидает вызова метода.
Blairg23,
0

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

Где элементов много, думаю нужно использовать "Словарь". Мой код:

NameValueCollection ProdIdes;
string prodId = _cfg.ProdIdes[key];
if (string.IsNullOrEmpty(prodId))
{
    ......
}

Или можно использовать это:

 string prodId = _cfg.ProdIdes[key] !=null ? "found" : "not found";
Роберто Гата
источник
0
queryItems.AllKeys.Contains(key)

Имейте в виду, что ключ может быть не уникальным и что сравнение обычно чувствительно к регистру. Если вы хотите просто получить значение первого совпадающего ключа и не беспокоиться о регистре, используйте это:

        public string GetQueryValue(string queryKey)
        {
            foreach (string key in QueryItems)
            {
                if(queryKey.Equals(key, StringComparison.OrdinalIgnoreCase))
                    return QueryItems.GetValues(key).First(); // There might be multiple keys of the same name, but just return the first match
            }
            return null;
        }
userSteve
источник
-1
NameValueCollection n = Request.QueryString;

if (n.HasKeys())
   {
       //something
   }

Возвращаемое значение Тип: System.Boolean true, если NameValueCollection содержит ключи, которые не равны нулю; в противном случае - ложь. ССЫЛКА НА САЙТ

Анант Дхас
источник
2
Хотя это может дать ответ на вопрос, часто приветствуются некоторые пояснения, особенно для объяснения того, как это может быть хорошей альтернативой многим другим ответам.
Pac0 07
Это только проверяет, содержит ли коллекция какие-либо ключи. ОП запросил наличие определенного ключа.
Билл Тюр