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

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.