Який найкращий спосіб перебрати словник?


2625

Я бачив кілька різних способів ітерації словника на C #. Чи є стандартний спосіб?


108
Я здивований, що є багато відповідей на це запитання, а також один, який було піднято 923 рази .. (використовує передбачення курсу). Я б заперечував або, принаймні, додав, що якщо вам доведеться перебирати словник, швидше за все, ви є використовуючи його неправильно / недоречно. Мені довелося зробити цей коментар, оскільки я бачив, як словники зловживають способами, які IMHO не підходять ... Так, можуть бути рідкісні обставини, коли ви повторюєте словник, а не шукаєте, що саме це розроблено для .. Будь ласка, майте це на увазі, перш ніж задаватися питанням, як перебрати словник.
Вікас Гупта

74
@VikasGupta Що б ви запропонували зробити щось із колекцією пар ключових значень, коли ви не знаєте, якими будуть ключі?
нач

26
@displayName Якщо ви хочете зробити щось із кожною парою ключ-значення, але у вас немає посилання на ключі, які слід шукати, ви переглянете словник, правда? Я просто вказував, що може трапитись час, коли ви хочете це зробити, незважаючи на твердження Вікаса, що це, як правило, неправильне використання.
nasch

34
Сказати, що це неправильне використання, означає, що є краща альтернатива. Що це за альтернатива?
Кайл Делані

40
VikasGupta помиляється, можу стверджувати, що після багатьох років високопродуктивного програмування C # та C ++ у нетеоретичних сценаріях. Дійсно бувають випадки, коли можна створити словник, зберігати унікальні пари ключів і значень, а потім повторювати ці значення, які, як доведено, мають унікальні ключі в колекції. Створення будь-яких подальших колекцій - це дійсно неефективний і дорогий спосіб уникнути ітерації словника. Будь ласка, надайте хорошу альтернативу як відповідь на питання, що роз'яснює вашу точку зору, інакше ваш коментар є досить безглуздим.
sɐunıɔ ןɐ qɐp

Відповіді:


3711
foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

29
Що робити, якщо я точно не знаю тип ключа / значення в словнику. Використання var entryкраще в цьому випадку, і , таким чином , я голосував цей відповідь на другий погляд замість вище.
Ozair Kafray

93
@OzairKafray, varколи ви не знаєте тип, як правило, погана практика.
Нейт

52
Ця відповідь є вищою, тому що Пабло не використовував за замовчуванням використання лінивого кодеру "var", що обдумує тип повернення.
MonkeyWrench

119
@MonkeyWrench: Мех. Visual Studio знає, що таке тип; все, що вам потрібно зробити, це навести курсор на змінну, щоб дізнатися це.
Роберт Харві

31
Як я розумію, varпрацює лише в тому випадку, якщо тип відомий під час компіляції. Якщо Visual Studio знає тип, то це також можна дізнатися.
Кайл Делані

866

Якщо ви намагаєтесь використовувати загальний словник у C #, як і ви, використовуєте асоціативний масив іншою мовою:

foreach(var item in myDictionary)
{
  foo(item.Key);
  bar(item.Value);
}

Або, якщо вам потрібно лише перебрати колекцію ключів, скористайтеся

foreach(var item in myDictionary.Keys)
{
  foo(item);
}

І нарешті, якщо вас цікавлять лише цінності:

foreach(var item in myDictionary.Values)
{
  foo(item);
}

(Візьміть до уваги, що varключове слово є необов'язковою функцією C # 3.0 і вище, ви також можете використовувати точний тип ваших ключів / значень тут)


11
функція var найбільше потрібна для першого блоку коду :)
nawfal

27
Я розумію, що ця відповідь вказує на те, що ви можете явно перебирати клавіші або значення.
Rotsiser Mho

11
Мені тут не подобається використання var. Враховуючи, що це просто синтаксичний цукор, навіщо його використовувати тут? Коли хтось намагається прочитати код, йому доведеться стрибнути навколо коду, щоб визначити тип myDictionary(якщо тільки це не є фактичною назвою). Я думаю, що використовувати var добре, коли тип очевидний, наприклад, var x = "some string"але коли це не відразу очевидно, я думаю, що це ліниве кодування, що шкодить читачеві / рецензенту коду
James Wierzba

8
varНа мою думку, слід використовувати економно. Особливо тут це не конструктивно: тип KeyValuePair, ймовірно, має відношення до питання.
Сінджай

3
varмає унікальне призначення, і я не вірю, що це «синтаксичний» цукор. Цілеспрямовано використовувати його - це відповідний підхід.
Джошуа К

159

У деяких випадках може знадобитися лічильник, який може бути наданий впровадженням циклу. Для цього LINQ надає ElementAtтакі можливості:

for (int index = 0; index < dictionary.Count; index++) {
  var item = dictionary.ElementAt(index);
  var itemKey = item.Key;
  var itemValue = item.Value;
}

21
Щоб використовувати метод '.ElementAt', пам’ятайте: використовуючи System.Linq; Це не включено в fx. автоматично створені тестові класи.
Тінія

14
Це шлях, якщо ви змінюєте значення, пов'язані з ключами. Інакше буде викинуто виняток під час модифікації та використання foreach ().
Майк де Клерк

27
Чи не ElementAtоперація O (n)?
Артуро Торрес Санчес

162
Ця відповідь цілком незаслужила стільки оновлень. У словнику немає неявного порядку, тому використання .ElementAtв цьому контексті може призвести до тонких помилок. Набагато серйозніше - точка Артуро вище. Ви будете dictionary.Count + 1повторювати словниковий час, що призводить до складності O (n ^ 2) для операції, яка повинна бути лише O (n). Якщо вам дійсно потрібен індекс (якщо це так, ви, ймовірно, в першу чергу використовуєте неправильний тип колекції), dictionary.Select( (kvp, idx) => new {Index = idx, kvp.Key, kvp.Value})замість цього слід повторити і не використовувати .ElementAtвсередині циклу.
витрачається

5
ElementAt - o (n) операція! Серйозно? Це приклад того, як ви не повинні цього робити. Ці багато оновлень?
Мукеш Адхварю

96

Залежить від того, чи будете ви після ключів або значень ...

З Dictionary(TKey, TValue)опису класу MSDN :

// When you use foreach to enumerate dictionary elements,
// the elements are retrieved as KeyValuePair objects.
Console.WriteLine();
foreach( KeyValuePair<string, string> kvp in openWith )
{
    Console.WriteLine("Key = {0}, Value = {1}", 
        kvp.Key, kvp.Value);
}

// To get the values alone, use the Values property.
Dictionary<string, string>.ValueCollection valueColl =
    openWith.Values;

// The elements of the ValueCollection are strongly typed
// with the type that was specified for dictionary values.
Console.WriteLine();
foreach( string s in valueColl )
{
    Console.WriteLine("Value = {0}", s);
}

// To get the keys alone, use the Keys property.
Dictionary<string, string>.KeyCollection keyColl =
    openWith.Keys;

// The elements of the KeyCollection are strongly typed
// with the type that was specified for dictionary keys.
Console.WriteLine();
foreach( string s in keyColl )
{
    Console.WriteLine("Key = {0}", s);
}

82

Взагалі, просити "найкращий спосіб" без конкретного контексту - це як запитати, що є найкращим кольором ?

З одного боку, є багато кольорів, і немає найкращого кольору. Це залежить від потреби, а часто і від смаку.

З іншого боку, існує багато способів ітерації словника в C #, і немає кращого способу. Це залежить від потреби, а часто і від смаку.

Найпростіший спосіб

foreach (var kvp in items)
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Якщо вам потрібно лише значення (дозволяє його називати item, читабельніше, ніж kvp.Value).

foreach (var item in items.Values)
{
    doStuff(item)
}

Якщо вам потрібен певний порядок сортування

Як правило, початківців дивує порядок перерахування словника.

LINQ пропонує стислий синтаксис, який дозволяє задавати порядок (та багато інших речей), наприклад:

foreach (var kvp in items.OrderBy(kvp => kvp.Key))
{
    // key is kvp.Key
    doStuff(kvp.Value)
}

Знову вам може знадобитися лише значення. LINQ також пропонує стисле рішення для:

  • ітерація безпосередньо за значенням (дозволяє викликати його item, читабельніше, ніж kvp.Value)
  • але відсортовано за клавішами

Ось:

foreach (var item in items.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value))
{
    doStuff(item)
}

Існує ще багато випадків використання в реальному світі, які можна зробити з цих прикладів. Якщо вам не потрібно конкретного замовлення, просто дотримуйтесь "найпростішого способу" (див. Вище)!


Останнє має бути, .Valuesа не обраним пунктом.
Мафія

@Mafii Ви впевнені? Значення, повернені OrderBy, не є типом KeyValuePair, у них немає Valueполя. Точний тип я бачу тут IOrderedEnumerable<KeyValuePair<TKey, TValue>>. Можливо, ви мали на увазі щось інше? Чи можете ви написати повний рядок із зазначенням того, що ви маєте на увазі (і перевірити це)?
Стефан Гурішон

Я думаю, що ця відповідь містить те, що я маю на увазі: stackoverflow.com/a/141105/5962841, але виправте мене, якщо я щось переплутав
Mafii

1
@Mafii Перечитайте всю свою відповідь, пояснення між розділами коду розповідають про контекст. Відповідь, яку ви згадуєте, подібна до другого розділу коду в моїй відповіді (замовлення не потрібно). Там я просто писав так, items.Valueяк ви запропонували. У випадку четвертого розділу, який ви прокоментували, Select()це спосіб викликати foreachперерахування безпосередньо значень у словнику замість пар ключ-значення. Якщо якимось чином вам не подобається Select()в цьому випадку, ви можете віддати перевагу третьому розділу коду. Суть четвертого розділу полягає в тому, щоб показати, що можна попередньо обробити колекцію за допомогою LINQ.
Стефан Гурішон

1
Якщо ви зробите це, .Keys.Orderby()ви повторіть список ключів. Якщо це все, що вам потрібно, добре. Якщо вам потрібні значення, то в циклі вам доведеться запитувати словник на кожній клавіші, щоб отримати значення. У багатьох сценаріях це практично не зміниться. У високопродуктивному сценарії це буде. Як я писав на початку відповіді: "Є багато способів (...) і немає найкращого способу. Це залежить від потреби, а часто і від смаку".
Стефан Гурішон

53

Я б сказав, що foreach - це стандартний спосіб, хоча це, очевидно, залежить від того, що ви шукаєте

foreach(var kvp in my_dictionary) {
  ...
}

Це те, що ви шукаєте?


42
Гм, чи не називання елемента "значення" досить заплутаним? Зазвичай ви використовуєте синтаксиси типу "value.Key" та "value.Value", що не дуже інтуїтивно зрозуміло для тих, хто читатиме код, особливо якщо вони не знайомі з тим, як реалізується .Net Dictionary. .
RenniePet

3
@RenniePet kvpзазвичай використовується для іменування примірників KeyValuePair при Перебір словників і пов'язаних з ними структур даних: foreach(var kvp in myDictionary){....
mbx

37

Ви також можете спробувати це у великих словниках для багатопотокової обробки.

dictionary
.AsParallel()
.ForAll(pair => 
{ 
    // Process pair.Key and pair.Value here
});

8
@WiiMaxx та ще важливіше, якщо ці елементи НЕ залежать один від одного
Мафій,

36

C # 7.0 представив Deconstructors, і якщо ви використовуєте .NET Core 2.0+ додаток, структураKeyValuePair<>вже міститьDeconstruct()для вас. Отже, ви можете зробити:

var dic = new Dictionary<int, string>() { { 1, "One" }, { 2, "Two" }, { 3, "Three" } };
foreach (var (key, value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}
//Or
foreach (var (_, value) in dic) {
    Console.WriteLine($"Item [NO_ID] = {value}");
}
//Or
foreach ((int key, string value) in dic) {
    Console.WriteLine($"Item [{key}] = {value}");
}

введіть тут опис зображення


5
Якщо ви використовуєте .NET Framework, у якого принаймні до 4.7.2 немає Deconstruct на KeyValuePair, спробуйте це:foreach (var (key, value) in dic.Select(x => (x.Key, x.Value)))
nwsmith

29

Я вдячний, що на це питання вже було багато відповідей, але я хотів трохи вивчити.

Ітерація через словник може бути досить повільною, порівняно з ітерацією над чимось на зразок масиву. У моїх тестах ітерація над масивом займала 0,015003 секунди, тоді як ітерація словника (з однаковою кількістю елементів) займала 0,0365073 секунди, що в 2,4 рази довше! Хоча я бачив набагато більші відмінності. Для порівняння, список опинився десь посеред 0,00215043 секунд.

Однак це як порівняння яблук та апельсинів. Моя думка, що ітерація над словниками повільна.

Словники оптимізовані для пошуку, тому маючи на увазі, я створив два методи. Один просто робить передбачення, інший повторює натискання клавіш.

public static string Normal(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var kvp in dictionary)
    {
        value = kvp.Value;
        count++;
    }

    return "Normal";
}

Цей завантажує ключі та замість них повторює (я також намагався втягнути ключі в рядок [], але різниця була незначною.

public static string Keys(Dictionary<string, string> dictionary)
{
    string value;
    int count = 0;
    foreach (var key in dictionary.Keys)
    {
        value = dictionary[key];
        count++;
    }

    return "Keys";
}

З цього прикладу звичайний тест передбачення взяв 0,0310062, а версія ключів - 0,2205441. Завантаження всіх клавіш і повторення через усі пошуки явно МНОГО повільніше!

Для остаточного тесту я провів свою ітерацію десять разів, щоб побачити, чи є користь від використання тут клавіш (до цього моменту мені було просто цікаво):

Ось метод RunTest, якщо це допоможе вам уявити, що відбувається.

private static string RunTest<T>(T dictionary, Func<T, string> function)
{            
    DateTime start = DateTime.Now;
    string name = null;
    for (int i = 0; i < 10; i++)
    {
        name = function(dictionary);
    }
    DateTime end = DateTime.Now;
    var duration = end.Subtract(start);
    return string.Format("{0} took {1} seconds", name, duration.TotalSeconds);
}

Тут звичайний пробіг передбачення зайняв 0,2820564 секунди (приблизно в десять разів довше, ніж за одну ітерацію - як можна було очікувати). Ітерація клавіш зайняла 2.2249449 секунд.

Відредаговано, щоб додати: Читання деяких інших відповідей змусило мене запитати, що буде, якщо я використовую словник замість словника. У цьому прикладі масив займав 0,0120024 секунди, список 0,0185037 секунд і словник 0,0465093 секунди. Доцільно розраховувати, що тип даних змінить, наскільки повільніший словник.

Які мої висновки ?

  • Якщо ви можете, уникайте ітерацій над словником, вони значно повільніші, ніж повторення масиву з тими ж даними.
  • Якщо ви вирішите перебрати словник, не намагайтеся бути занадто розумним, хоча повільніше ви можете зробити набагато гірше, ніж використовуючи стандартний метод foreach.

11
Ви повинні виміряти щось на кшталт StopWatch замість DateTime: hanselman.com/blog/…
Навіть

2
Ви можете, будь ласка, описати свій тестовий сценарій, скільки предметів, де у вашому словнику, як часто ви запускали свій сценарій для обчислення середнього часу, ...
WiiMaxx

3
Цікаво, що ви отримаєте різні результати залежно від того, які дані у словнику ви маєте. Під час ітерації над словником функція Enumerator повинна пропускати безліч порожніх слотів у словнику, і саме це призводить до того, що він проходить повільніше, ніж ітерація через масив. Якщо Словник заповнений, пропущених слотів буде менше, ніж якщо вони наполовину порожні.
Мартін Браун

26

Варіантів багато. Мій особистий фаворит - KeyValuePair

Dictionary<string, object> myDictionary = new Dictionary<string, object>();
// Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary)
{
     // Do some interesting things
}

Ви також можете використовувати колекції ключів і значень


14

З .NET Framework 4.7одним можна використовувати розкладання

var fruits = new Dictionary<string, int>();
...
foreach (var (fruit, number) in fruits)
{
    Console.WriteLine(fruit + ": " + number);
}

Щоб цей код працював на нижчих версіях C #, додайте System.ValueTuple NuGet packageта запишіть десь

public static class MyExtensions
{
    public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> tuple,
        out T1 key, out T2 value)
    {
        key = tuple.Key;
        value = tuple.Value;
    }
}

9
Це неправильно. .NET 4.7 просто ValueTupleвбудований. Він доступний як набір пакетів для більш ранніх версій. Що ще важливіше, C # 7.0+ потрібен, щоб Deconstructметод працював як деконструктор для var (fruit, number) in fruits.
Девід Арно

13

Станом на C # 7 ви можете деконструювати об'єкти на змінні. Я вважаю, що це найкращий спосіб перебрати словник.

Приклад:

Створіть метод розширення на KeyValuePair<TKey, TVal>тому, що деконструює його:

public static void Deconstruct<TKey, TVal>(this KeyValuePair<TKey, TVal> pair, out TKey key, out TVal value)
{
   key = pair.Key;
   value = pair.Value;
}

Повторіть будь Dictionary<TKey, TVal>-який наступним чином

// Dictionary can be of any types, just using 'int' and 'string' as examples.
Dictionary<int, string> dict = new Dictionary<int, string>();

// Deconstructor gets called here.
foreach (var (key, value) in dict)
{
   Console.WriteLine($"{key} : {value}");
}

10

Ви запропонували нижче повторити

Dictionary<string,object> myDictionary = new Dictionary<string,object>();
//Populate your dictionary here

foreach (KeyValuePair<string,object> kvp in myDictionary) {
    //Do some interesting things;
}

FYI, foreachне працює, якщо значення мають тип об'єкта.


4
Прохання уточнити: foreachне працюватиме , якщо який значення типу object? Інакше це не має особливого сенсу.
Марк Л.

9

Найпростіша форма для повторення словника:

foreach(var item in myDictionary)
{ 
    Console.WriteLine(item.Key);
    Console.WriteLine(item.Value);
}

9

Використовуючи C # 7 , додайте цей метод розширення до будь-якого проекту вашого рішення:

public static class IDictionaryExtensions
{
    public static IEnumerable<(TKey, TValue)> Tuples<TKey, TValue>(
        this IDictionary<TKey, TValue> dict)
    {
        foreach (KeyValuePair<TKey, TValue> kvp in dict)
            yield return (kvp.Key, kvp.Value);
    }
}


І використовуйте цей простий синтаксис

foreach (var(id, value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


Або цей, якщо хочете

foreach ((string id, object value) in dict.Tuples())
{
    // your code using 'id' and 'value'
}


На зміну традиційному

foreach (KeyValuePair<string, object> kvp in dict)
{
    string id = kvp.Key;
    object value = kvp.Value;

    // your code using 'id' and 'value'
}


Метод розширення перетворює KeyValuePairваш IDictionary<TKey, TValue>на сильно набранийtuple , що дозволяє використовувати цей новий зручний синтаксис.

Він перетворює -just- необхідні записи словника tuples, тому НЕ перетворює весь словник уtuples , тому не виникає жодних проблем щодо продуктивності.

Є лише незначна вартість виклику методу розширення для створення tupleв порівнянні з використанням KeyValuePairбезпосередньо, що НЕ повинно бути проблемою, якщо ви призначаєте KeyValuePairвластивості ' KeyіValue нові змінні циклу в будь-якому випадку.

На практиці цей новий синтаксис дуже добре підходить для більшості випадків, за винятком низькорівневих сценаріїв надвисокої продуктивності, де ви все ще маєте можливість просто не використовувати його у певному місці.

Перевірте це: Блог MSDN - Нові функції в C # 7


1
Що було б причиною віддати перевагу «зручним» кортежам перед парами «ключ-значення»? Я не бачу прибутку тут. Ваш кортеж містить ключ і значення, як і пара ключ-значення.
Маартен

4
Привіт Маартен, дякую за запитання. Основна перевага - читабельність коду без додаткових зусиль програмування. У KeyValuePair завжди слід використовувати форму kvp.Keyта kvp.Valueдля використання відповідно ключа та значення. За допомогою кортежів ви отримуєте можливість називати ключ і значення за вашим бажанням, не використовуючи подальших оголошень змінних всередині блоку foreach. Наприклад, ви можете назвати свій ключ як factoryName, а значення як models, що особливо корисно, коли ви отримуєте вкладені петлі (словники словників): обслуговування коду стає набагато простішим. Просто спробуйте! ;-)
sɐunıɔ ןɐ qɐp

7

Я знаю, що це дуже старе питання, але я створив кілька методів розширення, які можуть бути корисними:

    public static void ForEach<T, U>(this Dictionary<T, U> d, Action<KeyValuePair<T, U>> a)
    {
        foreach (KeyValuePair<T, U> p in d) { a(p); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.KeyCollection k, Action<T> a)
    {
        foreach (T t in k) { a(t); }
    }

    public static void ForEach<T, U>(this Dictionary<T, U>.ValueCollection v, Action<U> a)
    {
        foreach (U u in v) { a(u); }
    }

Таким чином я можу написати такий код:

myDictionary.ForEach(pair => Console.Write($"key: {pair.Key}, value: {pair.Value}"));
myDictionary.Keys.ForEach(key => Console.Write(key););
myDictionary.Values.ForEach(value => Console.Write(value););

6

Іноді, якщо вам потрібні лише перелічені значення, використовуйте колекцію значень словника:

foreach(var value in dictionary.Values)
{
    // do something with entry.Value only
}

Про це повідомляє цей пост, де зазначено, що це найшвидший метод: http://alexpinsker.blogspot.hk/2010/02/c-fastest-way-to-iterate-over.html


+1 для досягнення продуктивності. Дійсно, ітерація над самим словником включає деякі накладні витрати, якщо все, що вам потрібно, це значення. Це здебільшого пояснюється копіюванням значень чи посилань у структури KeyValuePair.
Яанус Варус

1
Останній тест у цьому посиланні - це перевірка неправильної речі: вона вимірює ітерацію над ключами, ігнорування значень, тоді як у попередніх двох тестах використовується значення.
Півмісяць Свіжий

5

Я знайшов цей метод у документації до класу DictionaryBase на MSDN:

foreach (DictionaryEntry de in myDictionary)
{
     //Do some stuff with de.Value or de.Key
}

Це було єдине, що мені вдалося правильно функціонувати в класі, який успадкував від DictionaryBase.


3
Це виглядає як при використанні негенеричної версії словника, тобто перед .NET Framework 2.0.
joedotnot

@ joed0tnot: це негенерична версія, яка використовується для Hashtableоб’єктів
sɐunıɔ ןɐ qɐp

5

foreachце найшвидше, і якщо ви лише повторіть ___.Values, це також швидше

введіть тут опис зображення


Чому ви телефонуєте ContainsKey()у forверсії? Це додає додаткових накладних витрат, яких немає в коді, з яким ви порівнюєте. TryGetValue()існує, щоб замінити точний шаблон "якщо ключ існує, отримати елемент з ключем". Крім того, якщо dictмістить суцільний діапазон цілих чисел від 0до dictCount - 1, ви знаєте, що індексатор не може вийти з ладу; в іншому випадку - dict.Keysце те, що ви повинні повторювати. У будь-якому випадку, немає ContainsKey()/ TryGetValue()необхідності. Останнє, будь ласка, не публікуйте скріншоти коду.
BACON

3

Я скористаюсь .NET 4.0+ та надаю оновлену відповідь на первісно прийнятий:

foreach(var entry in MyDic)
{
    // do something with entry.Value or entry.Key
}

3

За офіційною документацією на MSDN стандартним способом ітерації словника є:

foreach (DictionaryEntry entry in myDictionary)
{
     //Read entry.Key and entry.Value here
}

2

Я написав розширення, щоб перетворитися на словник.

public static class DictionaryExtension
{
    public static void ForEach<T1, T2>(this Dictionary<T1, T2> dictionary, Action<T1, T2> action) {
        foreach(KeyValuePair<T1, T2> keyValue in dictionary) {
            action(keyValue.Key, keyValue.Value);
        }
    }
}

Тоді ви можете зателефонувати

myDictionary.ForEach((x,y) => Console.WriteLine(x + " - " + y));

Ви визначили ForEachметод, у якому у вас є foreach (...) { }... Здається, непотрібним.
Маартен

1

Якщо ви скажете, ви хочете переглядати колекцію значень за замовчуванням, я вважаю, що ви можете реалізувати IEnumerable <>, де T - тип об'єкта значень у словнику, а "this" - словник.

public new IEnumerator<T> GetEnumerator()
{
   return this.Values.GetEnumerator();
}

1

Просто хотів додати свої 2 центри, оскільки більшість відповідей стосуються foreach-loop. Погляньте на наступний код:

Dictionary<String, Double> myProductPrices = new Dictionary<String, Double>();

//Add some entries to the dictionary

myProductPrices.ToList().ForEach(kvP => 
{
    kvP.Value *= 1.15;
    Console.Writeline(String.Format("Product '{0}' has a new price: {1} $", kvp.Key, kvP.Value));
});

Вважаючи, що це додає додатковий виклик ".ToList ()", можливо, відбудеться невелике покращення продуктивності (як вказувалося тут, передбачити проти someList.Foreach () {} ), espacially при роботі з великими словниками і працює паралельно немає варіант / взагалі не матиме ефекту.

Також зауважте, що ви не зможете призначити значення властивості "Value" всередині циклу foreach. З іншого боку, ви також зможете маніпулювати «Ключем», можливо, потрапляючи у вас проблеми під час виконання.

Коли ви просто хочете "прочитати" ключі та значення, ви також можете використовувати IEnumerable.Select ().

var newProductPrices = myProductPrices.Select(kvp => new { Name = kvp.Key, Price = kvp.Value * 1.15 } );

5
Копіювання всієї колекції без жодних причин не покращить продуктивність. Це різко уповільнить код, а також подвоїть слід пам'яті коду, який не повинен споживати додаткової пам'яті.
Сервіс

Я уникаю методу "List.ForEach" foreachпобічних ефектів : примушує видимість побічних ефектів угору, куди вона належить.
користувач2864740

0
var dictionary = new Dictionary<string, int>
{
    { "Key", 12 }
};

var aggregateObjectCollection = dictionary.Select(
    entry => new AggregateObject(entry.Key, entry.Value));

2
У цій відповіді потрібно більше обґрунтування / опису. Що AggregateObjectдодає KeyValuePair? Де "ітерація", як запитується у запитанні?
Марк Л.

Вибір ітерацій над словником і дозволяє нам працювати над кожним об’єктом. Це не так загально foreach, але я його багато використовував. Чи справді моя відповідь заслуговувала голосування?
Піксар

Ні, Select використовує ітерацію для отримання результату, але це не сам ітератор. Типи речей, для яких використовується ітерація ( foreach), особливо операції з побічними ефектами, - виходять за рамки Linq, у тому числі Select. Лямбда не буде працювати, поки aggregateObjectCollectionфактично не перераховано. Якщо ця відповідь сприймається як "перший шлях" (тобто використовується перед прямим foreach), це заохочує погані практики. Ситуаційно можуть виникати операції Linq, які є корисними перед ітерацією словника, але це не вирішує питання як задане.
Марк Л.

0

Словник <TKey, TValue> Це загальний клас колекції в c #, і він зберігає дані у форматі ключових значень. Ключ повинен бути унікальним, і він не може бути нульовим, тоді як значення може бути дублікатом і null. Як кожен елемент у словнику є трактується як KeyValuePair <TKey, TValue> структура, що представляє ключ та його значення. і, отже, нам слід прийняти тип елемента KeyValuePair <TKey, TValue> під час ітерації елемента. Нижче наведено приклад.

Dictionary<int, string> dict = new Dictionary<int, string>();
dict.Add(1,"One");
dict.Add(2,"Two");
dict.Add(3,"Three");

foreach (KeyValuePair<int, string> item in dict)
{
    Console.WriteLine("Key: {0}, Value: {1}", item.Key, item.Value);
}

0

Якщо ви хочете використовувати для циклу, ви можете зробити це:

var keyList=new List<string>(dictionary.Keys);
for (int i = 0; i < keyList.Count; i++)
{
   var key= keyList[i];
   var value = dictionary[key];
 }

У чому ж користь від цього? Це довший код, ніж foreachцикл і гірша продуктивність, тому що new List<string>(dictionary.Keys)ви повторите dictionary.Countчас, перш ніж у вас навіть з’явиться можливість повторити його. Відкидаючи, що прохання про "кращий спосіб" є суб'єктивним, я не бачу, як це може бути кваліфіковано як "найкращий спосіб", або "стандартний спосіб", якого прагне питання. До "Якщо ви хочете використовувати для циклу ...", я б порівнювався з " Не використовувати forцикл".
БАКОН

Якщо у вас велика колекція і операції проходять повільно, і якщо ваша колекція може змінитися при ітерації, вона захистить вас від помилки "колекція змінилася", і це рішення має кращу ефективність, ніж використання ElementAt.
Сечкін Дургай

Я погоджуюся, що виключення "Колекції було модифіковано" є винятками - це одна з причин цього, хоча цей особливий випадок не зазначався у запитанні, і це завжди можна зробити foreach (var pair in dictionary.ToArray()) { }. Тим не менш, я думаю, що було б добре відповісти у відповіді конкретний сценарій (и), у якому хотілося б використовувати цей код, і наслідки цього.
БАКОН

так, ти маєш рацію, але тут є відповідь у ElementAt, і вона має дуже високу репутацію, і я ввійшов у цю відповідь :)
Seçkin Durgay

-1

простий з linq

 Dictionary<int, string> dict = new Dictionary<int, string>();
 dict.Add(1, "American Ipa");
 dict.Add(2, "Weiss");
 dict.ToList().ForEach(x => Console.WriteLine($"key: {x.Key} | value: {x.Value}") );

Я бачу, що ви телефонуєте, ToList()тому що ForEach()це визначено лише на List<>уроці, але навіщо робити все це замість просто foreach (var pair in dict) { }? Я б сказав, що це ще простіше і не має однакових наслідків для пам'яті / продуктивності. Це точне рішення було запропоновано у цій відповіді ще 3,5 роки тому.
BACON

Просто заради візуалізації коду, це дійсно може бути зроблено так, як ви вказали, я просто хотів представити його по-іншому, і, по-моєму, вибачте, що не побачив вказану відповідь, обійняв
Луїс Фернандо Корреа Лейте

-3

на додаток до найвищого рейтингу, де існує дискусія між використанням

foreach(KeyValuePair<string, string> entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

або

foreach(var entry in myDictionary)
{
    // do something with entry.Value or entry.Key
}

Найповнішим є наступне, оскільки ви можете бачити тип словника при ініціалізації, kvp - KeyValuePair

var myDictionary = new Dictionary<string, string>(x);//fill dictionary with x

foreach(var kvp in myDictionary)//iterate over dictionary
{
    // do something with kvp.Value or kvp.Key
}

5
Створення та копіювання другого словника не є коректним рішенням читабельності коду. Насправді, я б стверджував, що це ускладнить розуміння коду, тому що тепер ви повинні запитати себе: "Чому останній хлопець створив другий словник?" Якщо ви хочете бути більш багатослівним, просто скористайтеся варіантом перший.
Меттью Гуларт

Я просто мав на увазі показати вам, що коли decl диктується раніше для кожного, використання в foreach зрозуміло з декларації
BigChaja
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.