Проверьте, существует ли ключ в 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или ""это должно помочь.


Извините, что это в 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;
        }

-1
NameValueCollection n = Request.QueryString;

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

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


2
Хотя это может дать ответ на вопрос, часто приветствуются некоторые пояснения, особенно для объяснения того, как это может быть хорошей альтернативой многим другим ответам.
— Pac0 07

Это только проверяет, содержит ли коллекция какие-либо ключи. ОП запросил наличие определенного ключа.
— Билл Тюр
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.