Überprüfen Sie, ob der Schlüssel in NameValueCollection vorhanden ist


141

Gibt es eine schnelle und einfache Möglichkeit, um zu überprüfen, ob ein Schlüssel in einer NameValueCollection vorhanden ist, ohne ihn zu durchlaufen?

Suchen Sie nach etwas wie Dictionary.ContainsKey () oder ähnlichem.

Es gibt natürlich viele Möglichkeiten, dies zu lösen. Ich frage mich nur, ob jemand helfen kann, mein Gehirn zu kratzen.


Verwenden Sie einfach Dictionary, wenn Sie eine Suche basierend auf dem Schlüssel durchführen möchten. Übrigens: Sie könnten den Indexer für diese Klasse verwenden, aber dies führt die Schleife selbst durch - also kein Gewinn
Carsten

Antworten:


181

Von MSDN :

Diese Eigenschaft gibt in den folgenden Fällen null zurück:

1) wenn der angegebene Schlüssel nicht gefunden wird;

Sie können also einfach:

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

2) wenn der angegebene Schlüssel gefunden wird und der zugehörige Wert null ist.

collection[key]ruft base.Get()dann auf, base.FindEntry()was intern Hashtablemit Leistung O (1) verwendet wird.


37
Diese Eigenschaft gibt in den folgenden Fällen null zurück: 1) wenn der angegebene Schlüssel nicht gefunden wird; und 2) wenn der angegebene Schlüssel gefunden wird und sein zugehöriger Wert null ist. Diese Eigenschaft unterscheidet nicht zwischen den beiden Fällen.
Steve

1
@Andreas deshalb ist es besser, leere Zeichenfolge statt null zu speichern
abatishchev

@Steve OP sagt nichts über eine solche Kollision aus.
Abatishchev

13
Richtig @abatishchev, aber das OP sagt "Überprüfen, ob ein Schlüssel vorhanden ist". Es ist nicht wahr, null als Schlüssel zu nehmen, der nicht existiert. Am Ende gibt es keine Antwort ohne Kompromisse (keine Schleife, leere Zeichenfolgen verwenden)
Steve

@abatishchev das ist wie zu sagen 0gleich null... sry
Andreas Niedermair

54

Verwenden Sie diese Methode:

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

    return true;
}

Es ist am effizientesten für NameValueCollectionund hängt nicht davon ab, ob die Sammlung nullWerte enthält oder nicht.


5
Denken Sie daran, using System.Linq;wenn Sie diese Lösung verwenden.
Jhauberg

14

Ich denke nicht, dass eine dieser Antworten ganz richtig / optimal ist. NameValueCollection unterscheidet nicht nur nicht zwischen Nullwerten und fehlenden Werten, sondern unterscheidet auch nicht zwischen Groß- und Kleinschreibung. Daher denke ich, dass eine vollständige Lösung wäre:

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

Ich mag diese Lösung. Wenn Sie sich die NameValueCollectionBase-Quelle ansehen, wird standardmäßig InvariantCultureIgnoreCase verwendet. Dies bedeutet jedoch nicht, dass keine andere übergeben wird, um stattdessen von einer beliebigen Klasse verwendet zu werden, die eine Instanz der NameValueCollection erstellt.
Lethek

12

Ja, Sie können Linq verwenden, um die AllKeysEigenschaft zu überprüfen :

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

Ein Dictionary<string, string[]>wäre jedoch weitaus besser für diesen Zweck geeignet, möglicherweise über eine Erweiterungsmethode erstellt:

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
Das wird die gesamte Sammlung durchlaufen, die O (n) ist. Während collection[key]intern verwendet, Hashtabledas ist O (1)
abatishchev

4
@abatishchev Tatsächlich wird jedoch collection[key]nicht zwischen dem nicht vorhandenen Schlüssel und einem für diesen Schlüssel gespeicherten Nullwert unterschieden.
Rich O'Kelly

Sie können auch einen Dirty Hack durchführen und mit Reflection ein privates Feld von Hashtable abrufen.
Abatishchev

Ich denke, das ist eine ziemlich dumme Lösung. Wenn jemand NameValueCollection verwendet, wird das Wörterbuch wahrscheinlich aus Gründen nicht unterstützt, z. B. weil er einen Nullschlüssel hat.
Chris Marisic

0

Sie können die GetMethode verwenden und prüfen, ob die nullMethode zurückgegeben wird, nullwenn die NameValueCollection den angegebenen Schlüssel nicht enthält.

Siehe MSDN .


Sie müssen wissen , indexder keydie Methode aufzurufen. Nicht wahr?
Abatishchev

0

Wenn die Sammlungsgröße klein ist, können Sie die von rich.okelly bereitgestellte Lösung verwenden. Eine große Sammlung bedeutet jedoch, dass die Generierung des Wörterbuchs möglicherweise merklich langsamer ist als nur das Durchsuchen der Schlüsselsammlung.

Wenn Ihr Verwendungsszenario zu verschiedenen Zeitpunkten nach Schlüsseln sucht, zu denen die NameValueCollection möglicherweise geändert wurde, kann das Generieren des Wörterbuchs jedes Mal langsamer sein als das Durchsuchen der Schlüsselsammlung.


0

Dies könnte auch eine Lösung sein, ohne eine neue Methode einführen zu müssen:

    item = collection["item"] != null ? collection["item"].ToString() : null;

0

Wie Sie sehen in den Bezugsquellen können, Namevaluecollection erbt von Nameobjectcollectionbase .

Sie nehmen also den Basistyp, rufen die private Hashtabelle per Reflektion ab und prüfen, ob sie einen bestimmten Schlüssel enthält.

Damit es auch in Mono funktioniert, müssen Sie den Namen der Hashtabelle in Mono sehen, was Sie hier sehen können (m_ItemsContainer), und das Monofeld abrufen, wenn die anfängliche FieldInfo null ist (Mono- Laufzeit).

So was

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

Für ultrareinen, nicht reflektierenden .NET 2.0-Code können Sie die Schlüssel durchlaufen, anstatt die Hash-Tabelle zu verwenden. Dies ist jedoch langsam.

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

In VB ist es:

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

In C # sollte nur sein:

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

Ich bin mir nicht sicher, ob es sein sollte nulloder ""aber dies sollte helfen.


Entschuldigung, das ist in VB. C # sollte nur if sein (MyNameValueCollection (Key)! = Null) {} Ich bin mir nicht sicher, ob es null oder "" sein soll, aber dies sollte helfen.
CodeShouldBeEasy

Ich glaube , die richtige Syntax zu einer ähnlich DictionaryDatenstruktur, wie in MyNameValueCollection[Key], statt MyNameValueCollection(Key), die erwartet einen Methodenaufruf.
Blairg23

0

Ich benutze diese Sammlung, als ich in der Sammlung kleiner Elemente gearbeitet habe.

Wo Elemente viel sind, denke ich, muss "Dictionary" verwendet werden. Mein Code:

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

Oder kann dies verwenden:

 string prodId = _cfg.ProdIdes[key] !=null ? "found" : "not found";

0
queryItems.AllKeys.Contains(key)

Beachten Sie, dass der Schlüssel möglicherweise nicht eindeutig ist und dass beim Vergleich normalerweise zwischen Groß- und Kleinschreibung unterschieden wird. Wenn Sie nur den Wert des ersten passenden Schlüssels erhalten möchten und sich nicht um die Groß- und Kleinschreibung kümmern möchten, verwenden Sie Folgendes:

        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
   }

Rückgabewert Typ: System.Boolean true, wenn die NameValueCollection Schlüssel enthält, die nicht null sind; sonst falsch. VERKNÜPFUNG


2
Während dies die Frage beantworten kann, werden oft einige Erklärungen geschätzt, insbesondere um zu erklären, wie dies eine gute Alternative zu den vielen anderen Antworten sein kann.
Pac0

Dies prüft nur, ob die Sammlung überhaupt Schlüssel enthält. Das OP bat um die Existenz eines bestimmten Schlüssels.
Bill Tür
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.