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?

Oliver
źródło
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];

}
jgauffin
źródło
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
Endy Tjahjono
źródło
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];
}
Sebastien Morin
źródło
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]);
        }
    }
Adriano Carneiro
źródło
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];
        }
DavidWainwright
źródło
0

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

 foreach (string akey in request.Query.Keys.Cast<string>())
     writer.WriteLine(akey + " = " + request.Query[akey]);
Namures
źródło
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;
        }
userSteve
źródło
-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

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