foreach KeyValuePair w NameValueCollection?


79

Mam ten kod:

NameValueCollection nv = HttpUtility.ParseQueryString(queryString);        
foreach (KeyValuePair<String,String> pr in nv) {
    //process KeyValuePair          
}

To się kompiluje, ale kiedy próbuję go uruchomić, pojawia się plik InvalidCastException.

Dlaczego to? Dlaczego nie mogę użyć KeyValuePairdo iteracji po a NameValueCollectioni czego powinienem użyć zamiast tego?


Podoba mi się to, ponieważ mogę zainicjować słownik bez konieczności tworzenia dodatkowej zmiennej słownika oprócz foreach. var nv = HttpUtility.ParseQueryString(Request.Url.Query); var qsDic = nv.Cast<object>().ToDictionary<object, string, object>(key => (string) key, key => nv[(string) key]);
— The Muffin Man,

Odpowiedzi:


133

Przede wszystkim NameValueCollectionnie używa KeyValuePair<String,String>. Ponadto foreachujawnia tylko klucz:

NameValueCollection nv = HttpUtility.ParseQueryString(queryString);        
foreach (string key in nv) {
    var value = nv[key];

}

3
Hmm, to trochę dziwne. Spodziewałbym się, że będzie sposób na uzyskanie obu naraz.
— Oliver

27
Wszyscy tak robimy, kiedy zaczynamy używać NVC.
— jgauffin,

1
Zastanawiałem się, jak to zrobić ... dzięki! Przy okazji, nie musisz określać, KeyValuePair<String,String>ponieważ NameValueCollection używa tylko ciągu, ciągu. Nie trzeba tego określać.
— TheGateKeeper

7
Należy pamiętać, jak ten kod działa w przypadku zduplikowanych kluczy. Jeśli istnieją zduplikowane klucze, zwróci „wartość klucza1, wartość2, wartość3” zamiast „wartość klucza1”, a następnie „wartość klucza2”, a następnie „wartość klucza3”.
— Doug S,

3
@Nick: NameValueCollection jest jednym z pierwszych typów kolekcji w .NET. Nie rozróżnia wielkości liter i łączy istniejące wartości (co jest przydatne w niektórych protokołach)
— jgauffin

12

Nie możesz tego zrobić bezpośrednio, ale możesz utworzyć metodę rozszerzenia w następujący sposób:

public static IEnumerable<KeyValuePair<string, string>> AsKVP(
        this NameValueCollection source
)
{
    return source.AllKeys.SelectMany(
        source.GetValues,
        (k, v) => new KeyValuePair<string, string>(k, v));
}

Następnie możesz:

NameValueCollection nv = HttpUtility.ParseQueryString(queryString);
foreach (KeyValuePair<String,String> pr in nv.AsKVP()) {
    //process KeyValuePair          
}

Uwaga: zainspirowany tym . SelectMany jest wymagane do obsługi zduplikowanych kluczy.

Wersja vb.net:

<Extension>
Public Function AsKVP(
        source As Specialized.NameValueCollection
) As IEnumerable(Of KeyValuePair(Of String, String))
    Dim result = source.AllKeys.SelectMany(
        AddressOf source.GetValues,
        Function(k, v) New KeyValuePair(Of String, String)(k, v))
    Return result
End Function

10

Na przyszłość możesz również użyć następującej składni:

foreach(string key in Request.QueryString)
{
    var value = Request.QueryString[key];
}

2
Dobrze. Jeśli masz dostęp do HttpRequest, możesz pominąć, AllKeysponieważ QueryStringnieruchomość to NameValueCollection.
— jgauffin

-1, ponieważ nie musisz używać, AllKeysjak wspomniano @jgauffin
— Warlock

@Warlock: Podanie -1 za to jest trochę trudne. Możesz edytować odpowiedź, prawda?
— jgauffin

Tak, przepraszam, nie chciałem nikogo skrzywdzić :) Ale problem dotyczył nie tylko nieruchomości. Jeśli spojrzysz na odpowiedź @ jgauffin, zaproponował on ten sam pomysł, ale został opublikowany wcześniej. Tak więc ten post wygląda jak duplikat.
— Warlock

Jeśli spojrzysz na ostatni komentarz, został on napisany przeze mnie, jgauffina odpowiedzi nie są duplikatami, jeśli przyjrzysz się bliżej.
— jgauffin

6

Inna metoda rozszerzenia do celów edukacyjnych:

    public static IEnumerable<KeyValuePair<string, string>> ToIEnumerable(this NameValueCollection nvc)
    {
        foreach (string key in nvc.AllKeys)
        {
            yield return new KeyValuePair<string, string>(key, nvc[key]);
        }
    }

Niezłe rozwiązanie! Sugerowałbym tylko użycie nvczamiast nvc.AllKeys. Zastanawiam się, dlaczego chłopaki z Microsoftu tego nie dodali System.Linq. :)
— Warlock

2

NameValueCollection używa oldskulowego modułu wyliczającego:

        var enu = ConfigurationManager.AppSettings.GetEnumerator();

        while(enu.MoveNext())
        {
            string key = (string)enu.Current;
            string value = ConfigurationManager.AppSettings[key];
        }

0

Tak mi się podobało i działa:

 foreach (string akey in request.Query.Keys.Cast<string>())
     writer.WriteLine(akey + " = " + request.Query[akey]);

0

Należy pamiętać, że nazwa klucza może pojawić się więcej niż raz w ciągu zapytania, a porównanie zwykle rozróżnia 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;
        }

-1
public static void PrintKeysAndValues2( NameValueCollection myCol )
{
    Console.WriteLine( "   [INDEX] KEY        VALUE" );
    for ( int i = 0; i < myCol.Count; i++ )
        Console.WriteLine( "   [{0}]     {1,-10} {2}", i, myCol.GetKey(i), myCol.Get(i) );
    Console.WriteLine();
}

http://msdn.microsoft.com/en-us/library/system.collections.specialized.namevaluecollection.aspx


-1 Pokazujesz, że można użyć for, aby uzyskać dostęp do NameValueCollectiondanych. Ale pytanie brzmi, dlaczego nie możesz konwertować NameValueCollectionelementów na KeyValuePair<string, string>. Może niezbyt uważnie przeczytałeś pytanie. Najlepszej odpowiedzi udzielił jgauffin. Ciekawe rozwiązanie zaproponował Adrian Carneiro.
— Warlock
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.