Dictionary c что это
Перейти к содержимому

Dictionary c что это

  • автор:

Под капотом у Dictionary и ConcurrentDictionary

Некоторое время назад, я решил, что хочу знать больше подробностей о работе многопоточности в .NET и что я уделял этому незаслуженно мало внимания в прошлом. Информации на эту тему великое множество (отправной точкой я для себя выбрал этот раздел книги «C# in a nutshell»), но, как оказалось, только малая часть ресурсов пытаются объяснить что-то в деталях.

Каждый мастер должен знать свои инструменты, а что может использоваться чаще коллекций? Поэтому я решил сделать небольшой обзор многопоточных коллекций и начать с ConcurrentDictionary (беглый обзор уже встречался здесь, но его там совсем мало). Вообще, я несколько удивился, что такой статьи для .NET еще нет (зато хватает по Java).

Итак, поехали.

Если вы уже знакомы с самим Dictionary, то можете пропустить следующий раздел.

Что такое Dictionary?

Dictionary представляет собой реализацию стандартной Hashtable.
Здесь интересны следующие функции:

Инициализация

Инициализация происходит либо при создании (если передана начальный размер коллекции), либо при добавлении первого элемента, причем в качестве размера будет выбрано ближайшее простое число (3). При этом создаются 2 внутренние коллекции — int[] buckets и Entry[] entries. Первая будет содержать индексы элементов во второй коллекции, а она, в свою очередь, — сами элементы в таком виде:

private struct Entry
Добавление элементов

При добавлении элемента вычисляется хэшкод его ключа и затем — индекс корзины в которую он будет добавлен по модулю от величины коллекции:

int bucketNum = (hashcode & 0x7fffffff) % capacity; 

Выглядеть это будет примерно так:

Затем проверяется нет ли уже такого ключа в коллекции, если есть — то операция Add выбросит исключение, а присваивание по индексу просто заменит элемент на новый. Если достигнут максимальный размер словаря, то происходит расширение (выбирается новый размер ближайшим простым числом).
Сложность оперции соответственно — O(n).

Если происходит коллизия (то есть в корзине с индексов bucketNum уже есть элемент), то новый элемент добавляется в коллекцию, его индекс сохраняется в корзине, а индекс старого элемента — в его поле next.

Таким образом получаем однонаправленный связный список. Данный механизм разрешения коллизий называется chaining. Если при добавлении элемента число коллизий велико (больше 100 в текущей версии), то при расширении коллекции происходит операция перехэширования, перед выполнением которой случайным образом выбирается новый генератор хэшкодов.
Сложность добавления O(1) или O(n) в случае коллизии.

Удаление элементов

При удалении элементов мы затираем его содержимое значениями по умолчанию, меняем указатели next других элементов при неоходимости и сохраняем индекс этого элемента во внутреннее поле freeList, а старое значение — в поле next. Таким образом, при добавлении нового элемента мы можем повторно использовать такие свободные ячейки:

Сложность снова O(1) или O(n) в случае коллизии.

Другое

Так же стоит отметить 2 момента:
1) При очистке словаря, его внутренний размер не изменяется. То есть, потенциально, вы просто тратите место.
2) GetEnumerator просто возвращает итератор по коллекции entires (сложность O(1)). Если вы только добавляли элементы — они вернутся в том же порядке. Однако если вы удаляли элементы и добавляли новые — порядок соответственно изменится, поэтому на него полагаться не стоит (тем более, что в будущих версиях фреймворка это может измениться).

Так и что же с ConcurrentDictionary?

Казалось бы, есть 2 решения в лоб для обеспечения потокобезопасности — обернуть все обращения к словарю в блокировки или обернуть все его методы в них же. Однако, по понятным причинам, такое решение будет медленным — задержки, добавляемые lock, да и ограничение на 1 поток, который мог бы работать с коллекцией не добавляют быстродействия.

В Microsoft пошли более оптимальным путем и Dictionary притерпел некоторые изменения. Так, благодаря внутренней структуре словаря и его корзин, блокировка осуществляется по ним, с помощью метода

private void GetBucketAndLockNo(int hashcode, out int bucketNo, out int lockNo, int bucketCount, int lockCount)

В то же время обычный словарь не смог бы работать с этой схемой, потому что все корзины используют один и тот же массив entries, поэтому корзины стали представлять собой обычный single linked list: volatile Entry[] m_buckets (поле объявлено как volitale, чтобы обеспечить неблокирующую синхронизацию в ситуации когда один поток пытается выполнить какую-то операцию, а другой в этот момент изменяет размер коллекции).

В итоге корзины стали выглядеть вот так:

lockNo — это индекс в новом массиве, который содержит объекты синхронизации — object[] m_locks. Его использование позволяет разным потокам изменять разные корзины в одно и то же время. Размер этой коллекции зависит от параметра ConcurrencyLevel который можно задать через конструктор. Он определяет примерное число потоков которые будут одновременное работать с коллекцией (по умолчанию это число_процессоров * 4). Чем выше это значение, тем проще будут происходить операции записи, но так же станут гораздо дороже операции, которые требуют полной блокировки всей коллекции (Resize, ToArray, Count, IsEmpty, Keys, Values, CopyTo, Clear). Также этот параметр определяет сколько элементов коллекции приходится на один lock (как отношение числа корзин к размеру этой коллекции) и когда элементов становится больше, чем надо — коллекция расширяется, потому что в противном случае поиск элемента требует не O(1), а уже O(n) за счет обхода связных списков. Чтобы немного снизить число первоначальных расширений коллекции, начальный размер словаря уже не 3, а 31.

Все операции приобрели вид:

void ChangeBacket(TKey key) < while (true) < Node[] buckets = m_buckets; int bucketNo, lockNo; GetBucketAndLockNo(m_comparer.GetHashCode(key), out bucketNo, out lockNo, buckets.Length); lock (m_locks[lockNo]) < if (buckets != m_buckets) < // Race condition. Пока мы ждали блокировки, другой поток расширил коллекцию. continue; >Node node = m_buckets[bucketNo]; // Вносим изменения в нод. > > > 

При добавлении нового элемента приходится обойти весь связанный список просто чтобы определить есть ли уже такой ключ и если нет — добавить. Аналогично при удалении — сперва надо найти узел который удалить.

Впрочем, для некоторых операций блокировка не нужна в принципе — это TryGetValue, GetEnumerator, ContainsKey и индексация. Почему? Потому что все изменения размера корзин видны за счет того что поле volatile, любые добавления или изменения элементов происходят путем создания нового элемента и замены им старого, а удаления происходят просто заменой указателя на следующий узел.

Другое

1) В отличие от обычного Dictionary, вызов метода Clear, сбрасывает размер коллекции на значение по умолчанию.
2) GetEnumerator — может возвращать старые значения в случае, если изменения были сделаны другим потоком после вызова метода и того как итератор прошел этот элемент. В упомянутой в начале статье было отмечено, что

лучше использовать dictionary.Select(x => x.Value).ToArray(), чем dictionary.Values.ToArray()

И это не совсем верно — вместо «лучше» тут должно быть «быстрее» — за счет отсутствия блокировок, но надо принять во внимание, что данные подходы могут вернуть разные данные.

Dictionary c что это

Еще один распространенный тип коллекции представляют словари. Словарь хранит объекты, которые представляют пару ключ-значение. Класс словаря Dictionary типизируется двумя типами: параметр K представляет тип ключей, а параметр V предоставляет тип значений.

Создания и инициализация словаря

Класс Dictionary предоставляет ряд конструкторов для создания словаря. Например, мы можем создать пустой словарь:

Dictionary people = new Dictionary();

Здесь словарь people в качестве ключей принимает значения типа int, а в качестве значений — строки.

При определении словаря его сразу же можно инициализировать значениями:

var people = new Dictionary() < < 5, "Tom">, < 3, "Sam">, < 11, "Bob">>;

При инициализации применяется инициализитор — в фигурных скобках после вызова конструктора объекту передаются начальные данные. В случае со словаем мы можем передать в инициализаторе набор элементов, где каждый элемент заключается в фигурные скобки, например:

Каждый элемент представляет два значения: первое значение представляет ключ, а второе значение — собственно значение элемента. Поскольку при объявлении словаря people для ключей указан тип int , а для значений — тип string , то в элементе словаря сначала указывается число int, а затем строка. То есть в случае выше элемент имеет ключ 5, а значение — «Tom». Затем по ключу элемента мы сможем получить его значение.

Также мы можем применять другой способ инициализации:

var people = new Dictionary() < [5] = "Tom", [6] = "Sam", [7] = "Bob" >;

При таком способе инициализации в квадратных скобках указывается ключ и ему присваивается значение элемента. Но в целом этот способ инициализации будет равноценен предыдущему.

KeyValuePair

Стоит отметить, что каждый элемент в словаре представляет структуру KeyValuePair , где параметр TKey представляет тип ключа, а параметр TValue — тип значений элементов. Эта структура предоставляет свойства Key и Value , с помощью которых можно получить соответственно ключ и значение элемента в словаре. И одна из версий конструктора Dictionary позволяет инициализировать словарь коллекцией объектов KeyValuePair:

var mike = new KeyValuePair(56, "Mike"); var employees = new List() < mike>; var people = new Dictionary(employees);

Конструктор типа KeyValuePair принимает два параметра — ключ элемента и его значения. То есть в данном случае создается один такой элемент — mike с ключом 56 и значением «Mike». И этот элемент добавляется в список employees, которым затем инициализируется словарь.

Можно совместить оба способа инициализации:

var mike = new KeyValuePair(56, "Mike"); var employees = new List() < mike >; var people = new Dictionary(employees) < [5] = "Tom", [6] = "Sam", [7] = "Bob", >;

В данном случае в словаре people будет четыре элемента.

Перебор словаря

Для перебора словаря можно применять цикл foreach :

var people = new Dictionary() < [5] = "Tom", [6] = "Sam", [7] = "Bob" >; foreach(var person in people) < Console.WriteLine($"key: value: "); >

При переборе каждый элемент будет помещаться в переменную, которая представляет тип KeyValuePair, соответственно с помощью свойств Key и Value мы сможем получить ключ и значение элемента. Консольный вывод программы:

key: 5 value: Tom key: 6 value: Sam key: 7 value: Bob

Получение элементов

Для обращения к элементам из словаря применяется их ключ, который передается в квадратных скобках:

словарь[ключ]

Таким образом мы можем получить и изменить элементы словаря

var people = new Dictionary() < [5] = "Tom", [6] = "Sam", [7] = "Bob", >; // получаем элемент по ключу 6 string sam = people[6]; // Sam Console.WriteLine(sam); // Sam // переустанавливаем значение по ключу 6 people[6] = "Mike"; Console.WriteLine(people[6]); // Mike // добавляем новый элемент по ключу 22 people[22] = "Eugene"; Console.WriteLine(people[22]); // Eugene

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

Методы и свойства Dictionary

Среди методов класса Dictionary можно выделить следующие:

  • void Add(K key, V value) : добавляет новый элемент в словарь
  • void Clear() : очищает словарь
  • bool ContainsKey(K key) : проверяет наличие элемента с определенным ключом и возвращает true при его наличии в словаре
  • bool ContainsValue(V value) : проверяет наличие элемента с определенным значением и возвращает true при его наличии в словаре
  • bool Remove(K key) : удаляет по ключу элемент из словаря Другая версия этого метода позволяет получить удленный элемент в выходной параметр: bool Remove(K key, out V value)
  • bool TryGetValue(K key, out V value) : получает из словаря элемент по ключу key. При успешном получении передает значение элемента в выходной параметр value и возвращает true
  • bool TryAdd(K key, V value) : добавляет в словарь элемент с ключом key и значением value. При успешном добавлении возвращает true

Из свойств следует отметить свойство Count , которое возвращает количество элементов в словаре.

// условная телефонная книга var phoneBook = new Dictionary(); // добавляем элемент: ключ - номер телефона, значение - имя абонента phoneBook.Add("+123456", "Tom"); // альтернативное добавление // phoneBook["+123456"] = "Tom"; // Проверка наличия var phoneExists1 = phoneBook.ContainsKey("+123456"); // true Console.WriteLine($"+123456: "); var phoneExists2 = phoneBook.ContainsKey("+567456"); // false Console.WriteLine($"+567456: "); var abonentExists1 = phoneBook.ContainsValue("Tom"); // true Console.WriteLine($"Tom: "); var abonentExists2 = phoneBook.ContainsValue("Bob"); // false Console.WriteLine($"Bob: "); // удаление элемента phoneBook.Remove("+123456"); // проверяем количество элементов после удаления Console.WriteLine($"Count: "); // Count: 0

Эффективное использование словаря (C#) как альтернатива оператору If

Всё больше наших коллег на практике предпочитают избегать применения операторов if . Эти условия, по их мнению, усложняют и прибавляют багов в наши приложения.

Но условия лежат в основе программирования, и мы не можем полностью избавиться от операторов if — мы можем лишь сократить их использование.

Что такое словарь?

Словарь — это структура данных, предназначенная для хранения группы объектов. Он может использоваться для маппинга, кэша в оперативной памяти, таблиц и т.д. Объекты хранятся в нём как коллекция пар ключ/значение, что очень удобно и характерно для разных объектно-ориентированных языков программирования. Таким же образом — в виде пар ключ/значение — могут быть заданы делегаты.

Что такое делегат?

«Делегат — это объект, который ссылается на метод. Или даже можно сказать, что это переменная ссылочного типа, которая содержит ссылку на методы. Делегаты в C# схожи с указателем на функцию в C/C++. Он помогает определить, какой метод должен вызываться при срабатывании события».

Есть два типа делегатов, которые нужны для наших примеров: Action и Func. Action используется для методов void, а Func — для методов возвращаемого типа return.

static void Main(string[] args)
Dictionary dict = new Dictionary();
dict.Add("foo", bar);
dict["foo"].Invoke();

Console.WriteLine("World");

Console.ReadLine();
>

static void bar()
Console.WriteLine("Hello");
>

Здесь определяется словарь с типом и к нему добавляется элемент, а затем выполняется метод для вызова dict[“foo”] . Можем использовать dict[“foo”]() как альтернативу dict[“foo”].Invoke() .

Какой полезный метод, правда?:)

Пример

Предположим, у нас есть модуль генерации отчётов, который создаёт периодические отчёты: ежедневные, еженедельные, ежемесячные, ежегодные и т.д. Причём они не содержат параметрического или возвращаемого типа.

Сначала он выглядит вот так:

using System;
using System.Collections.Generic;

namespace DictionaryTraining
public class Program
static void Main(string[] args)
Reporter reporter = new Reporter();
ReportType reportType = ReportType.Monthly;

PrepareReport(reportType);

Console.ReadLine();
>

private static void PrepareReport(ReportType reportType)
Reporter reporter = new Reporter();

if (reportType == ReportType.Daily)
reporter.GetDailyReport();
>
else if (reportType == ReportType.Weekly)
reporter.GetWeeklyReport();
>
else if (reportType == ReportType.Monthly)
reporter.GetMonthlyReport();
>
else if (reportType == ReportType.Annual)
reporter.GetAnnualReport();
>

>
>

public class Reporter
public void GetDailyReport()
Console.WriteLine("Daily report is preparing. ");
>

public void GetWeeklyReport()
Console.WriteLine("Weekly report is preparing. ");
>

public void GetMonthlyReport()
Console.WriteLine("Monthly report is preparing. ");
>

public void GetAnnualReport()
Console.WriteLine("Annual report is preparing. ");
>
>

public enum ReportType
Daily,
Weekly,
Monthly,
Annual
>
>

Теперь у нас есть класс Reporter с разными методами для подготовки отчётов.

Для вызова определённого метода в соответствии с типом каждого отчёта используется метод PrepareReport . Для этого будет задействовано множество операторов if-else .

Ну а мы попробуем вместо них использовать словарь и делегаты:

using System;
using System.Collections.Generic;

namespace DictionaryTraining
public class Program
static Dictionary dictReports = new Dictionary();
static void Main(string[] args)
Reporter reporter = new Reporter();
dictReports.Add(ReportType.Daily, new Action(reporter.GetDailyReport));
dictReports.Add(ReportType.Weekly, new Action(reporter.GetWeeklyReport));
dictReports.Add(ReportType.Monthly, new Action(reporter.GetMonthlyReport));
dictReports.Add(ReportType.Annual, new Action(reporter.GetAnnualReport));

dictReports[ReportType.Weekly]();

Console.ReadLine();
>
>

public class Reporter
public void GetDailyReport()
Console.WriteLine("Daily report is preparing. ");
>

public void GetWeeklyReport()
Console.WriteLine("Weekly report is preparing. ");
>

public void GetMonthlyReport()
Console.WriteLine("Monthly report is preparing. ");
>

public void GetAnnualReport()
Console.WriteLine("Annual report is preparing. ");
>
>

enum ReportType
Daily,
Weekly,
Monthly,
Annual
>
>

Словарь с типом будет использоваться в качестве делегата, а методы создания отчёта будут вызываться без необходимости проверять тип каждого отчёта.

На мой взгляд, это более читаемый и лёгкий в сопровождении код. Меньше строк — больше ясности.

Делегаты с возвращаемым типом

Если методы для подготовки отчётов имеют возвращаемый тип, должен использоваться делегат Func (но все методы должны иметь те же параметрические и возвращаемый типы).

using System;
using System.Collections.Generic;

namespace DictionaryTraining
public class Program
static Dictionary> dictReports = new Dictionary>();
static void Main(string[] args)
Reporter reporter = new Reporter();
dictReports.Add(ReportType.Daily, new Func(reporter.GetDailyReport));
dictReports.Add(ReportType.Weekly, new Func(reporter.GetWeeklyReport));
dictReports.Add(ReportType.Monthly, new Func(reporter.GetMonthlyReport));
dictReports.Add(ReportType.Annual, new Func(reporter.GetAnnualReport));

dictReports[ReportType.Daily](60);

Console.ReadLine();
>
>

public class Reporter
public string GetDailyReport(int dayOfYear)
Console.WriteLine("Daily report is preparing. ");
return "report of day: " + dayOfYear;
>

public string GetWeeklyReport(int weekOfYear)
Console.WriteLine("Weekly report is preparing. ");
return "report of week: " + weekOfYear;
>

public string GetMonthlyReport(int monthOfYear)
Console.WriteLine("Monthly report is preparing. ");
return "report of month: " + monthOfYear;
>

public string GetAnnualReport(int year)
Console.WriteLine("Annual report is preparing. ");
return "report of year: " + year;
>
>

enum ReportType
Daily,
Weekly,
Monthly,
Annual
>
>

И вот результат:

А сколько ещё есть вариантов применения словаря с точки зрения роста показателей производительности!

  • Разбираемся с компилятором Go
  • Строковые методы в Python
  • Топ-10 самых распространенных ошибок в проектах Go. Часть 1

Словарь: класс Dictionary

представляет собой сложную структуру данных, позволяющую обеспечить доступ к элементам по ключу. Главное свойство словарей — быстрый поиск на основе ключей. Можно также свободно добавлять и удалять элементы, подобно тому, как это делается в List, но без накладных расходов производительности, связанных с необходимостью смещения последующих элементов в памяти.

На следующем рисунке представлена упрощенная модель словаря. Здесь ключами словаря служат идентификаторы сотрудников, такие как В4711. Ключ трансформируется в хеш. В хеше создается число для ассоциации индекса со значением. После этого индекс содержит ссылку на значение. Изображенная модель является упрощенной, поскольку существует возможность того, что единственное вхождение индекса может быть ассоциировано с несколькими значениями, и индекс может храниться в виде дерева.

Модель словаря C#

В .NET Framework предлагается несколько классов словарей. Главный класс, который можно использовать — это Dictionary.

Тип ключа

Тип, используемый в качестве ключа словаря, должен переопределять метод GetHashCode() класса Object. Всякий раз, когда класс словаря должен найти местоположение элемента, он вызывает метод GetHashCode().

Целое число, возвращаемое этим методом, используется словарем для вычисления индекса, куда помещен элемент. Мы не станем углубляться в подробности работы этого алгоритма. Единственное, что следует знать — это то, что он использует простые числа, так что емкость словаря всегда выражается простым числом.

Реализация метода GetHashCode() должна удовлетворять перечисленным ниже требованиям:

  • Один и тот же объект должен всегда возвращать одно и то же значение.
  • Разные объекты могут возвращать одно и то же значение.
  • Он должен выполняться насколько возможно быстро, не требуя значительных вычислительных затрат.
  • Он не должен генерировать исключений.
  • Он должен использовать как минимум одно поле экземпляра.
  • Значения хеш-кода должны распределяться равномерно по всему диапазону чисел, которые может хранить int.
  • Хеш-код не должен изменяться на протяжении времени существования объекта.

Чем вызвана необходимость равномерного распределения значений хеш-кода по диапазону целых чисел? Если два ключа возвращают хеш-значения, дающие один и тот же индекс, класс словаря вынужден искать ближайшее доступное свободное место для сохранения второго элемента, к тому же ему придется выполнять некоторый поиск, чтобы впоследствии извлечь требуемое значение. Понятно, что это наносит ущерб производительности, и если множество ключей дают одни и те же индексы, куда их следует поместить, вероятность конфликтов значительно возрастает. Однако благодаря способу, которым работает часть алгоритма, принадлежащая Microsoft, риск снижается до минимума, когда вычисляемое значение хеш-кода равномерно распределено между int.MinValue и int.MaxValue.

Помимо реализации GetHashCode() тип ключа также должен реализовывать метод IEquatable.Equals() либо переопределять метод Equals() класса Object. Поскольку разные объекты ключа могут возвращать один и тот же хеш-код, метод Equals() используется при сравнении ключей словаря. Словарь проверяет два ключа А и В на эквивалентность, вызывая A.Equals(В). Это означает, что потребуется обеспечить истинность следующего утверждения:

Если истинно А.Equals(В) , значит, А.GetHashCode() и В.GetHashCode() всегда должны возвращать один и тот же хеш-код.

Класс Dictionary

В классе Dictionary реализуются интерфейсы IDictionary, IDictionary, ICollection, ICollection, IEnumerable, IEnumerable, ISerializable и IDeserializationCallback. В двух последних интерфейсах поддерживается сериализация списка. Словари имеют динамический характер, расширяясь по мере необходимости.

В классе Dictionary предоставляется немало конструкторов. Ниже перечислены наиболее часто используемые из них:

public Dictionary() public Dictionary(IDictionary dictionary) public Dictionary(int capacity)

В первом конструкторе создается пустой словарь с выбираемой по умолчанию первоначальной емкостью. Во втором конструкторе создается словарь с указанным количеством элементов dictionary. А в третьем конструкторе с помощью параметра capacity указывается емкость коллекции, создаваемой в виде словаря. Если размер словаря заранее известен, то, указав емкость создаваемой коллекции, можно исключить изменение размера словаря во время выполнения, что, как правило, требует дополнительных затрат вычислительных ресурсов.

В классе Dictionary определяется также ряд методов:

Add()

Добавляет в словарь пару «ключ-значение», определяемую параметрами key и value. Если ключ key уже находится в словаре, то его значение не изменяется, и генерируется исключение ArgumentException

ContainsKey()

Возвращает логическое значение true, если вызывающий словарь содержит объект key в качестве ключа; а иначе — логическое значение false

ContainsValue()

Возвращает логическое значение true, если вызывающий словарь содержит значение value; в противном случае — логическое значение false

Remove()

Удаляет ключ key из словаря. При удачном исходе операции возвращается логическое значение true, а если ключ key отсутствует в словаре — логическое значение false

Кроме того, в классе Dictionary определяются собственные свойства, помимо тех, что уже объявлены в интерфейсах, которые в нем реализуются. Эти свойства приведены ниже:

Comparer

Получает метод сравнения для вызывающего словаря

Keys

Получает коллекцию ключей

Values

Получает коллекцию значений

Следует иметь в виду, что ключи и значения, содержащиеся в коллекции, доступны отдельными списками с помощью свойств Keys и Values. В коллекциях типа Dictionary.KeyCollection и Dictionary.ValueCollection реализуются как обобщенные, так и необобщенные формы интерфейсов ICollection и IEnumerable.

И наконец, в классе Dictionary реализуется приведенный ниже индексатор, определенный в интерфейсе IDictionary

public TValue this[TKey key]

Этот индексатор служит для получения и установки значения элемента коллекции, а также для добавления в коллекцию нового элемента. Но в качестве индекса в данном случае служит ключ элемента, а не сам индекс. При перечислении коллекции типа Dictionary из нее возвращаются пары «ключ-значение» в форме структуры KeyValuePair. Напомним, что в этой структуре определяются два поля.

public TKey Key; public TValue Value;

В этих полях содержится ключ или значение соответствующего элемента коллекции. Как правило, структура KeyValuePair не используется непосредственно, поскольку средства класса Dictionary позволяют работать с ключами и значениями по отдельности. Но при перечислении коллекции типа Dictionary, например, в цикле foreach перечисляемыми объектами являются пары типа KeyValuePair.

Давайте рассмотрим пример использования словарей:

using System; using System.Collections.Generic; namespace ConsoleApplication1 < class UserInfo < // Метод, реализующий словарь public static DictionaryMyDic(int i) < Dictionarydic = new Dictionary(); Console.WriteLine("Введите имя сотрудника: \n"); string s; for (int j = 0; j < i; j++) < Console.Write("Name--> ",j); s = Console.ReadLine(); dic.Add(j, s); Console.Clear(); > return dic; > > class Program < static void Main() < Console.Write("Сколько сотрудников добавить? "); try < int i = int.Parse(Console.ReadLine()); Dictionarydic = UserInfo.MyDic(i); // Получить коллекцию ключей ICollection keys = dic.Keys; Console.WriteLine("База данных содержит: "); foreach (int j in keys) Console.WriteLine("ID -> Name -> ",j,dic[j]); > catch (FormatException) < Console.WriteLine("Неверный ввод"); >Console.ReadLine(); > > > 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *