Verifique se a chave existe no NameValueCollection

141

Existe uma maneira rápida e simples de verificar se existe uma chave em um NameValueCollection sem passar por ela?

Procurando algo como Dictionary.ContainsKey () ou semelhante.

Existem muitas maneiras de resolver isso, é claro. Imaginando se alguém pode ajudar a coçar meu cérebro.

Muhammad Yussuf
fonte
basta usar dicionário, se você quiser fazer uma pesquisa com base na chave .... BTW: você poderia usar o indexador desta classe, mas isso vai fazer a própria looping - assim nenhum ganho
Carsten

Respostas:

181

Do MSDN :

Esta propriedade retorna nulo nos seguintes casos:

1) se a chave especificada não for encontrada;

Então você pode apenas:

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

2) se a chave especificada for encontrada e seu valor associado for nulo.

collection[key]chama base.Get()então base.FindEntry()que internamente usa Hashtablecom desempenho O (1).

abatishchev
fonte
37
Esta propriedade retorna nulo nos seguintes casos: 1) se a chave especificada não for encontrada; e 2) se a chave especificada for encontrada e seu valor associado for nulo. Esta propriedade não faz distinção entre os dois casos.
26612 Steve Steve
1
@Andreas é por isso que é melhor para armazenar cadeia vazia em vez de nulo
abatishchev
@ Steve OP não diz nada sobre essa colisão.
23412 abatishchev
13
Certo @abatishchev, no entanto, o OP diz 'verificando se existe uma chave'. Tomar nulo como chave não existe não é verdade. No final não há nenhuma resposta sem um compromisso (nenhum loop, usar cadeias vazias)
Steve
@abatishchev que é como dizer 0igual a null... sry
Andreas Niedermair
54

Use este método:

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

    return true;
}

É o mais eficiente NameValueCollectione não depende de a coleção conter nullvalores ou não.

Kirill Polishchuk
fonte
5
Lembre-se de using System.Linq;quando usar esta solução.
precisa saber é o seguinte
14

Não acho que nenhuma dessas respostas esteja correta / ideal. NameValueCollection não apenas não distingue entre valores nulos e valores ausentes, mas também faz distinção entre maiúsculas e minúsculas em relação às chaves. Assim, acho que uma solução completa seria:

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
fonte
Eu gosto desta solução. Observando a origem NameValueCollectionBase, o padrão é usar InvariantCultureIgnoreCase, no entanto, isso não significa que uma diferente não seja passada para uso em vez disso por qualquer classe que crie uma instância da NameValueCollection.
Lethek 9/10/2015
12

Sim, você pode usar o Linq para verificar a AllKeyspropriedade:

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

No entanto, um Dictionary<string, string[]>seria muito mais adequado a esse propósito, talvez criado por meio de um método de extensão:

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
fonte
1
Isso fará um loop sobre toda a coleção que é O (n). Enquanto collection[key]internamente usos Hashtableque é O (1)
abatishchev
4
@abatishchev De fato, no entanto, collection[key]não faz distinção entre a chave não estar presente e um valor nulo sendo armazenado nessa chave.
22612 Rich O'Kelly
Além disso, você pode pré-executar um hack sujo e recuperar o campo privado do Hashtable usando o Reflection.
abatishchev
Eu acho que essa é uma solução bem boba. Se alguém estiver usando NameValueCollection, é provável que o dicionário não seja suportado, como uma chave nula.
precisa saber é o seguinte
0

Você pode usar o Getmétodo e verificar se nullo método retornará nullse o NameValueCollection não contiver a chave especificada.

Veja MSDN .

Codrin Eugeniu
fonte
Você precisa saber indexdo keyque chamar o método. Não é?
abatishchev
0

Se o tamanho da coleção for pequeno, você poderá usar a solução fornecida por rich.okelly. No entanto, uma grande coleção significa que a geração do dicionário pode ser notavelmente mais lenta do que apenas pesquisar na coleção de chaves.

Além disso, se seu cenário de uso estiver pesquisando chaves em diferentes momentos, onde o NameValueCollection pode ter sido modificado, a geração do dicionário a cada vez poderá ser mais lenta do que apenas pesquisar a coleção de chaves.

Jaguar
fonte
0

Isso também pode ser uma solução sem a necessidade de introduzir um novo método:

    item = collection["item"] != null ? collection["item"].ToString() : null;
buraco de código
fonte
0

Como você pode ver nas fontes de referência, NameValueCollection herda de NameObjectCollectionBase .

Então você escolhe o tipo base, obtém a hashtable privada por meio de reflexão e verifica se ela contém uma chave específica.

Para que ele funcione também em mono, é necessário ver qual é o nome da hashtable em mono, que é algo que você pode ver aqui (m_ItemsContainer) e obter o mono-campo, se o FieldInfo inicial for nulo (mono- tempo de execução).

Como isso

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 código .NET 2.0 não puro e não refletivo ultra-puro, você pode fazer um loop sobre as chaves, em vez de usar a tabela de hash, mas isso é 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
fonte
0

No VB é:

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

Em C # deve ser apenas:

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

Não tenho certeza se deve ser nullou ""mas isso deve ajudar.

CodeShouldBeEasy
fonte
Desculpe que está no VB. C # deve ser apenas se (MyNameValueCollection (Key)! = Null) {} Não tenho certeza se deve ser nulo ou "", mas isso deve ajudar.
CodeShouldBeEasy
Eu acredito que a sintaxe correta é semelhante a uma Dictionaryestrutura de dados, como em MyNameValueCollection[Key], e não MyNameValueCollection(Key), que espera uma chamada de método.
Blairg23
0

Estou usando esta coleção, quando trabalhei na coleção de pequenos elementos.

Onde os elementos são muito, acho que preciso usar "Dicionário". Meu código:

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

Ou pode ser usado isso:

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

Esteja ciente de que a chave pode não ser única e que a comparação geralmente diferencia maiúsculas de minúsculas. Se você deseja apenas obter o valor da primeira chave correspondente e não se preocupar com maiúsculas e minúsculas, use o seguinte:

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

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

Valor de retorno Tipo: System.Boolean trueSe o NameValueCollection contiver chaves que não são nulas; caso contrário, false. LIGAÇÃO

Anant Dhas
fonte
2
Embora isso possa responder à pergunta, algumas explicações geralmente são apreciadas, especialmente para explicar como essa pode ser uma boa alternativa para muitas outras respostas.
Pac0
Isso verifica apenas se a coleção contém alguma chave. O OP solicitou a existência de uma determinada chave.
Bill Tür