foreach KeyValuePair в NameValueCollection?

79

У меня есть такой код:

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

Это компилируется, но когда я пытаюсь запустить его, я получаю InvalidCastException .

Почему это? Почему я не могу использовать KeyValuePairдля итерации по a NameValueCollection, и что мне использовать вместо этого?

Оливер
источник
Мне это нравится, так как я могу инициализировать словарь без необходимости создавать переменную вспомогательного словаря в дополнение к 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

Ответы:

133

Во-первых, NameValueCollectionне использует KeyValuePair<String,String>. Кроме того, foreachраскрывает только ключ:

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

}
Джгауффин
источник
3
Хм, это как-то странно. Я ожидал, что есть способ получить и то, и другое одновременно.
Оливер
27
Все мы делаем это, когда начинаем использовать NVC.
jgauffin 05
1
Интересно, как это сделать ... спасибо! Кстати, вам не нужно указывать, KeyValuePair<String,String>поскольку NameValueCollection использует только строку, строку. Нет необходимости указывать это.
TheGateKeeper,
7
Помните, как этот код работает для повторяющихся ключей. Если существуют повторяющиеся ключи, он вернет «значение ключа1, значение2, значение3» вместо «значение ключа1», затем «значение ключа2», затем «значение ключа3».
Doug S
3
@Nick: NameValueCollection - один из первых типов коллекций в .NET. Он нечувствителен к регистру и объединяет существующие значения (что полезно для некоторых протоколов)
jgauffin
12

Вы не можете сделать это напрямую, но вы можете создать такой метод расширения:

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

Тогда вы сможете:

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

Примечание: вдохновлено этим . SelectMany требуется для обработки повторяющихся ключей.

версия 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

Для справки в будущем вы также можете использовать этот синтаксис:

foreach(string key in Request.QueryString)
{
    var value = Request.QueryString[key];
}
Себастьян Морен
источник
2
Хорошо. Если у вас есть доступ к сайту, HttpRequestвы можете пропустить его, AllKeysпоскольку это QueryStringсвойство NameValueCollection.
jgauffin
-1, потому что вам не нужно использовать, AllKeysкак упоминал @jgauffin
Warlock
@Warlock: Ставить -1 за это немного сурово. Вы ведь можете отредактировать ответ?
jgauffin
Да, извините, не хотел никого обидеть :) Но проблема была не только в собственности. Если вы посмотрите ответ @ jgauffin, он предложил ту же идею, но она была опубликована ранее. Итак, этот пост выглядит как дубликат.
Warlock
Если вы посмотрите на последний комментарий, он написан мной, jgauffinи ответы не будут дублироваться, если вы присмотритесь.
jgauffin
6

Другой метод расширения для учебных целей:

    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]);
        }
    }
Адриано Карнейро
источник
Хорошее решение! Я бы просто предложил использовать nvcвместо nvc.AllKeys. Мне интересно, почему ребята из Microsoft не добавили это System.Linq. :)
Warlock
2

NameValueCollection использует перечислитель old-skool:

        var enu = ConfigurationManager.AppSettings.GetEnumerator();

        while(enu.MoveNext())
        {
            string key = (string)enu.Current;
            string value = ConfigurationManager.AppSettings[key];
        }
Дэвид Уэйнрайт
источник
0

Мне это понравилось, и это работает:

 foreach (string akey in request.Query.Keys.Cast<string>())
     writer.WriteLine(akey + " = " + request.Query[akey]);
Namures
источник
0

Имейте в виду, что имя ключа может встречаться в строке запроса более одного раза и что при сравнении обычно учитывается регистр.
Если вы хотите просто получить значение первого совпадающего ключа и не беспокоиться о регистре, используйте это:

        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
источник
-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 Вы показываете, что можно использовать для доступа к NameValueCollectionданным. Но вопрос в том, почему нельзя конвертировать NameValueCollectionпредметы в KeyValuePair<string, string>. Может вы не очень внимательно прочитали вопрос. Лучший ответ дал jgauffin. Интересное решение предложил Adrian Carneiro.
Warlock