Compruebe si la clave existe en NameValueCollection

141

¿Hay una manera rápida y simple de verificar si existe una clave en una NameValueCollection sin recorrerla?

Buscando algo como Dictionary.ContainsKey () o similar.

Hay muchas formas de resolver esto, por supuesto. Solo me pregunto si alguien puede ayudar a rascarme la picazón del cerebro.

Muhammad Yussuf
fuente
solo use Dictionary si desea hacer una búsqueda basada en la clave ... Por cierto: podría usar el indexador en esta clase pero esto hará el bucle en sí mismo, por lo que no hay ganancia
Carsten

Respuestas:

181

De MSDN :

Esta propiedad devuelve nulo en los siguientes casos:

1) si no se encuentra la clave especificada;

Entonces puedes simplemente:

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

2) si se encuentra la clave especificada y su valor asociado es nulo.

collection[key]llama a base.Get()continuación, base.FindEntry()que utiliza internamente Hashtablecon el rendimiento O (1).

abatishchev
fuente
37
Esta propiedad devuelve nulo en los siguientes casos: 1) si no se encuentra la clave especificada; y 2) si se encuentra la clave especificada y su valor asociado es nulo. Esta propiedad no distingue entre los dos casos.
Steve
1
@Andreas por eso es mejor almacenar una cadena vacía en lugar de nula
abatishchev
@Steve OP no dice nada sobre tal colisión.
abatishchev
13
Derecha @abatishchev, sin embargo, el OP dice 'comprobar si existe una clave'. Tomar nulo como clave no existe no es cierto. Al final no hay respuesta sin compromiso (sin bucle, use cadenas vacías)
Steve
@abatishchev es como decir 0igual null... sry
Andreas Niedermair
54

Usa este método:

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

    return true;
}

Es el más eficiente NameValueCollectiony no depende de si la colección contiene nullvalores o no.

Kirill Polishchuk
fuente
55
Recuerde hacerlo using System.Linq;cuando use esta solución.
jhauberg
14

No creo que ninguna de estas respuestas sea correcta / óptima. NameValueCollection no solo no distingue entre valores nulos y valores perdidos, sino que también distingue entre mayúsculas y minúsculas con respecto a sus claves. Por lo tanto, creo que una solución completa sería:

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);
}
ChaseMedallion
fuente
Me gusta esta solución Al mirar la fuente NameValueCollectionBase, el valor predeterminado es el uso de InvariantCultureIgnoreCase, sin embargo, eso no significa que una clase diferente crea una instancia de NameValueCollection.
lethek
12

Sí, puede usar Linq para verificar la AllKeyspropiedad:

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

Sin embargo, a Dictionary<string, string[]>sería mucho más adecuado para este propósito, tal vez creado mediante un método de extensión:

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))
{
   ...
}
Rich O'Kelly
fuente
1
Eso recorrerá toda la colección que es O (n). Mientras collection[key]usa internamente Hashtablecuál es O (1)
abatishchev
44
@abatishchev De hecho, sin embargo, no collection[key]hace distinción entre la clave que no está presente y un valor nulo almacenado en esa clave.
Rich O'Kelly
También puede realizar un truco sucio y recuperar el campo privado de Hashtable usando Reflection.
abatishchev
Creo que esta es una solución bastante tonta. Si alguien está usando NameValueCollection, es probable que por razones que el diccionario no sea compatible, como tener una clave nula.
Chris Marisic
0

Puede usar el Getmétodo y verificarlo, nullya que el método devolverá nullsi NameValueCollection no contiene la clave especificada.

Ver MSDN .

Codrin Eugeniu
fuente
Lo que necesita saber indexde la keyque llamar al método. Tu no?
abatishchev
0

Si el tamaño de la colección es pequeño, puede optar por la solución proporcionada por rich.okelly. Sin embargo, una gran colección significa que la generación del diccionario puede ser notablemente más lenta que solo buscar en la colección de claves.

Además, si su escenario de uso está buscando claves en diferentes puntos en el tiempo, donde NameValueCollection puede haber sido modificado, generar el diccionario cada vez puede, una vez más, ser más lento que simplemente buscar en la colección de claves.

Jaguar
fuente
0

Esto también podría ser una solución sin tener que introducir un nuevo método:

    item = collection["item"] != null ? collection["item"].ToString() : null;
agujero de codys
fuente
0

Como puede ver en las fuentes de referencia, NameValueCollection hereda de NameObjectCollectionBase .

Entonces, toma el tipo base, obtiene la tabla hash privada a través de la reflexión y verifica si contiene una clave específica.

Para que funcione también en Mono, debe ver cuál es el nombre de la tabla hash en mono, que es algo que puede ver aquí (m_ItemsContainer), y obtener el campo mono, si FieldInfo inicial es nulo (mono- tiempo de ejecución).

Me gusta esto

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);
    }
}

para el código .NET 2.0 ultrapuro no reflectante, puede recorrer las teclas, en lugar de usar la tabla hash, pero eso es lento.

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;
}
Stefan Steiger
fuente
0

En VB es:

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

En C # solo debería ser:

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

No estoy seguro de si debería ser nullo debería ser ""útil.

CodeShouldBeEasy
fuente
Lo siento, eso está en VB. C # debería ser if (MyNameValueCollection (Key)! = Null) {} No estoy seguro si debería ser nulo o "" pero esto debería ayudar.
CodeShouldBeEasy
Creo que la sintaxis correcta es similar a una Dictionaryestructura de datos, como en MyNameValueCollection[Key], en lugar de MyNameValueCollection(Key), que espera una llamada al método.
Blairg23
0

Estoy usando esta colección, cuando trabajé en la colección de elementos pequeños.

Cuando los elementos son muchos, creo que necesito usar "Diccionario". Mi código:

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

O se puede usar esto:

 string prodId = _cfg.ProdIdes[key] !=null ? "found" : "not found";
Roberto Gata
fuente
0
queryItems.AllKeys.Contains(key)

Tenga en cuenta que la clave puede no ser única y que la comparación suele ser sensible a mayúsculas y minúsculas. Si desea obtener el valor de la primera clave coincidente y no se preocupa por el caso, use esto:

        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
fuente
-1
NameValueCollection n = Request.QueryString;

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

Tipo de valor de retorno: System.Boolean verdadero si NameValueCollection contiene claves que no son nulas; de lo contrario, falso. ENLACE

Anant Dhas
fuente
2
Si bien esto puede responder la pregunta, a menudo se agradece alguna explicación, especialmente para explicar cómo esta puede ser una buena alternativa a las muchas otras respuestas.
Pac0
Esto solo verifica si la colección contiene alguna clave. El OP solicitó la existencia de cierta clave.
Bill Tür