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

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

-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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.