Sprawdź, czy klucz istnieje w NameValueCollection

141

Czy istnieje szybki i prosty sposób sprawdzenia, czy klucz istnieje w NameValueCollection bez przechodzenia przez niego?

Szukasz czegoś takiego jak Dictionary.ContainsKey () lub podobnego.

Oczywiście istnieje wiele sposobów rozwiązania tego problemu. Zastanawiam się tylko, czy ktoś może pomóc mi w zadrapaniu swędzenia mózgu.

Muhammad Yussuf
źródło
wystarczy użyć słownika, jeśli chcesz zrobić odnośnika na podstawie klucza .... BTW: Państwo mogli skorzystać z indeksowania w tej klasie, ale to zrobi to zapętlenie się - tak bez zysku
Carsten

Odpowiedzi:

181

Z MSDN :

Ta właściwość zwraca wartość null w następujących przypadkach:

1) jeśli określony klucz nie zostanie znaleziony;

Możesz więc po prostu:

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

2) jeśli określony klucz zostanie znaleziony i skojarzona z nim wartość to null.

collection[key]wywołuje base.Get()wtedy, base.FindEntry()które wewnętrznie używa Hashtablez wydajnością O (1).

abatishchev
źródło
38
Ta właściwość zwraca wartość null w następujących przypadkach: 1) jeśli określony klucz nie zostanie znaleziony; oraz 2) jeśli określony klucz zostanie znaleziony, a skojarzona z nim wartość to null. Ta właściwość nie rozróżnia tych dwóch przypadków.
Steve
1
@Andreas dlatego lepiej jest przechowywać pusty łańcuch zamiast zerowego
abatishchev
@Steve OP nic nie mówi o takiej kolizji.
abatishchev
13
Po prawej @abatishchev, jednak OP mówi „sprawdzanie, czy klucz istnieje”. Przyjmowanie wartości null, ponieważ klucz nie istnieje, nie jest prawdą. Na koniec nie ma odpowiedzi bez kompromisu (brak pętli, użyj pustych ciągów)
Steve
@abatishchev to jak powiedzenie 0równa się null... sry
Andreas Niedermair
54

Użyj tej metody:

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

    return true;
}

Jest to najbardziej wydajne dla NameValueCollectioni nie zależy od tego, czy kolekcja zawiera nullwartości, czy nie.

Kirill Polishchuk
źródło
5
Pamiętaj o using System.Linq;korzystaniu z tego rozwiązania.
jhauberg
14

Nie sądzę, aby żadna z tych odpowiedzi była całkiem poprawna / optymalna. NameValueCollection nie tylko nie rozróżnia wartości null i brakujących wartości, ale także nie rozróżnia wielkości liter w odniesieniu do swoich kluczy. Dlatego uważam, że pełnym rozwiązaniem byłoby:

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
źródło
Podoba mi się to rozwiązanie. Patrząc na źródło NameValueCollectionBase, domyślnie używa InvariantCultureIgnoreCase, jednak nie oznacza to, że inny nie jest przekazywany do użycia zamiast tego przez jakąkolwiek klasę, która tworzy wystąpienie NameValueCollection.
lethek
12

Tak, możesz skorzystać z Linq, aby sprawdzić AllKeysnieruchomość:

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

Jednak Dictionary<string, string[]>znacznie bardziej nadaje się do tego celu, być może utworzony za pomocą metody rozszerzającej:

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
źródło
1
To zapętli całą kolekcję, która jest O (n). Podczas gdy collection[key]wewnętrznie używa HashtableO (1)
abatishchev
4
@abatishchev Rzeczywiście, jednak collection[key]nie rozróżnia między brakiem klucza a wartością zerową przechowywaną w tym kluczu.
Rich O'Kelly
Możesz także wykonać brudny hack i odzyskać prywatne pole Hashtable za pomocą Reflection.
abatishchev
Myślę, że to dość głupie rozwiązanie. Jeśli ktoś używa NameValueCollection, prawdopodobnie jest to spowodowane tym, że słownik nie jest obsługiwany, na przykład posiadanie klucza o wartości null.
Chris Marisic
0

Możesz użyć Getmetody i sprawdzić, nullponieważ metoda zwróci, nulljeśli NameValueCollection nie zawiera określonego klucza.

Zobacz MSDN .

Codrin Eugeniu
źródło
Trzeba wiedzieć indexz keywywołać metodę. Prawda?
abatishchev
0

Jeśli rozmiar kolekcji jest mały, możesz wybrać rozwiązanie dostarczone przez rich.okelly. Jednak duża kolekcja oznacza, że ​​generowanie słownika może być zauważalnie wolniejsze niż tylko przeszukiwanie kolekcji kluczy.

Ponadto, jeśli Twój scenariusz użycia wyszukuje klucze w różnych punktach w czasie, w których może zostać zmodyfikowana kolekcja NameValueCollection, generowanie słownika za każdym razem może być wolniejsze niż zwykłe przeszukiwanie kolekcji kluczy.

Jaguar
źródło
0

Może to być również rozwiązanie bez konieczności wprowadzania nowej metody:

    item = collection["item"] != null ? collection["item"].ToString() : null;
codys-hole
źródło
0

Jak widać w źródłach referencyjnych, NameValueCollection dziedziczy po NameObjectCollectionBase .

Więc bierzesz typ podstawowy, uzyskujesz prywatną tablicę haszującą przez odbicie i sprawdzasz, czy zawiera określony klucz.

Aby to działało również w Mono, musisz zobaczyć, jaka nazwa tablicy hashy jest w mono, co można zobaczyć tutaj (m_ItemsContainer), i uzyskać mono-pole, jeśli początkowe FieldInfo ma wartość null (mono- runtime).

Lubię to

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

w przypadku ultra-czystego, nieodblaskowego kodu .NET 2.0, możesz zapętlić klucze, zamiast używać tablicy mieszania, ale jest to powolne.

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
źródło
0

W VB to:

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

W C # powinno być po prostu:

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

Nie jestem pewien, czy powinien to być nullalbo ""ale to powinno pomóc.

CodeShouldBeEasy
źródło
Przepraszam, że jest w VB. C # powinno być po prostu if (MyNameValueCollection (Key)! = Null) {} Nie wiem, czy powinno to być null, czy „”, ale to powinno pomóc.
CodeShouldBeEasy
Uważam, że poprawna składnia jest podobna do Dictionarystruktury danych, jak w MyNameValueCollection[Key], a nie MyNameValueCollection(Key), która oczekuje wywołania metody.
Blairg
0

Korzystam z tej kolekcji, kiedy pracowałem w kolekcji małych elementów.

Tam, gdzie jest dużo elementów, myślę, że trzeba użyć „Słownika”. Mój kod:

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

Lub może użyć tego:

 string prodId = _cfg.ProdIdes[key] !=null ? "found" : "not found";
Roberto Gata
źródło
0
queryItems.AllKeys.Contains(key)

Należy pamiętać, że klucz może nie być unikatowy, a porównanie zwykle uwzględnia wielkość liter. Jeśli chcesz po prostu uzyskać wartość pierwszego pasującego klucza i nie przejmować się wielkością liter, użyj tego:

        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
źródło
-1
NameValueCollection n = Request.QueryString;

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

Typ wartości zwracanej: System.Boolean true, jeśli NameValueCollection zawiera klucze, które nie mają wartości null; w przeciwnym razie fałsz. POŁĄCZYĆ

Anant Dhas
źródło
2
Chociaż może to odpowiedzieć na pytanie, często docenia się pewne wyjaśnienia, szczególnie w celu wyjaśnienia, w jaki sposób może to być dobra alternatywa dla wielu innych odpowiedzi.
Pac0
Sprawdza tylko, czy kolekcja zawiera w ogóle jakiekolwiek klucze. PO poprosił o istnienie określonego klucza.
Bill Tür