foreach KeyValuePair en NameValueCollection?

79

Tengo este codigo:

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

Esto se compila, pero cuando intento ejecutarlo obtengo un archivo InvalidCastException.

¿Por qué es esto? ¿Por qué no puedo usar KeyValuePairpara iterar sobre a NameValueCollection, y qué debo usar en su lugar?

Oliver
fuente
Me gusta esto porque puedo iniciar un diccionario sin tener que crear una variable de diccionario de respaldo además de un 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

Respuestas:

133

En primer lugar, NameValueCollectionno usa KeyValuePair<String,String>. Además, foreachsolo expone la clave:

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

}
jgauffin
fuente
3
Hmm, eso es un poco extraño. Hubiera esperado que hubiera una manera de obtener ambos a la vez.
Oliver
27
Todos lo hacemos cuando empezamos a usar un NVC.
jgauffin
1
Me preguntaba cómo hacer esto ... ¡gracias! Por cierto, no es necesario especificarlo, KeyValuePair<String,String>ya que NameValueCollection solo usa cadena, cadena. No es necesario especificarlo.
TheGateKeeper
7
Tenga en cuenta cómo funciona este código para claves duplicadas. Cuando existen claves duplicadas, devolverá "clave valor1, valor2, valor3", en lugar de "clave valor1" luego "clave valor2" luego "clave valor3".
Doug S
3
@Nick: NameValueCollection es uno de los primeros tipos de colección en .NET. Es sensible a mayúsculas y los valores de combinación existente (que es útil para algunos protocolos)
jgauffin
12

No puede hacer eso directamente, pero puede crear un método de extensión como este:

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

Entonces puedes hacer:

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

Nota: inspirado en esto . Se requiere SelectMany para manejar claves duplicadas.

Versión de 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
fuente
10

Para referencia futura, también puede usar esta sintaxis:

foreach(string key in Request.QueryString)
{
    var value = Request.QueryString[key];
}
Sebastien Morin
fuente
2
Bien. Si tiene acceso a la HttpRequestpuede omitir AllKeysya que la QueryStringpropiedad es una NameValueCollection.
jgauffin
-1 porque no tienes que usar AllKeyscomo mencionó @jgauffin
Warlock
@Warlock: Dar un -1 por eso es un poco duro. Puedes editar la respuesta, ¿no?
jgauffin
Sí, lo siento, no quería lastimar a nadie :) Pero el problema no estaba solo en la propiedad. Si miras la respuesta de @jgauffin, propuso la misma idea, pero se publicó antes. Entonces, esta publicación parece una duplicación.
Warlock
Si miras el último comentario, está escrito por mí jgauffiny las respuestas no están duplicadas si miras más de cerca.
jgauffin
6

Otro método de extensión, con fines de aprendizaje:

    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
fuente
¡Buena solución! Solo sugeriría usar en nvclugar de nvc.AllKeys. Me pregunto por qué los chicos de Microsoft no agregaron esto System.Linq. :)
Warlock
2

NameValueCollection usa el enumerador de skool antiguo:

        var enu = ConfigurationManager.AppSettings.GetEnumerator();

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

Me gustó esto y funciona:

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

Tenga en cuenta que el nombre de la clave puede aparecer más de una vez en la cadena de consulta y que la comparación suele distinguir entre mayúsculas y minúsculas.
Si solo desea obtener el valor de la primera clave coincidente y no se preocupa por el caso, use esto:

        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
fuente
-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

silbido
fuente
-1 Muestra que es posible utilizar for para acceder a los NameValueCollectiondatos. Pero la pregunta es por qué no puede convertir NameValueCollectionelementos a KeyValuePair<string, string>. Quizás leíste la pregunta sin mucha atención. La mejor respuesta la dio jgauffin. Una interesante solución fue propuesta por Adrian Carneiro.
Brujo