Предыдущая лекция |   | Следующая лекция :----------------:|:----------:|:----------------: [Операторы и операции языка.](./articles/t3l1_2.md) | [Содержание](../readme.md#тема-4-программирование-на-языке-c-основы) | [Строки.](./articles/4_prog_string.md) # Основы языка C# * [Массивы](#Массивы) * [Многомерные массивы](#Многомерные-массивы) * [Класс Array](#Класс-Array) * [Массивы в качестве параметров](#Массивы-в-качестве-параметров) * [Коллекции](#Коллекции) * [ArrayList](#ArrayList) * [List](#List) * [Dictionary](#Dictionary) ## Массивы Массив представляет собой совокупность переменных ***одного типа*** с общим для обращения к ним именем. В C# массивы могут быть как одномерными, так и многомерными. Массивы служат самым разным целям, поскольку они предоставляют удобные средства для объединения связанных вместе переменных. Массивами в C# можно пользоваться практически так же, как и в других языках программирования. Тем не менее у них имеется одна особенность: они реализованы в виде объектов. Для тoго чтобы воспользоваться массивом в программе, требуется двухэтапная процедура, поскольку в C# массивы реализованы в виде объектов. Во-первых, необходимо объявить переменную, которая может обращаться к массиву. Например, команда `int[] arrayOfInteger` объявляет переменную с именем *ArrayOfInteger* с типом `int[]` - одномерный массив целых чисел. И во-вторых, нужно создать экземпляр массива, используя оператор *new*. ```cs // Объявляем массив int[] myArr = new int[5]; // Инициализируем каждый элемент массива вручную myArr[0] = 100; myArr[1] = 23; myArr[2] = 25; myArr[3] = 31; myArr[4] = 1; foreach (int i in myArr) Console.WriteLine(i); Console.ReadLine(); ``` Следует иметь в виду, что если массив только объявляется, но явно не инициализируется, каждый его элемент будет установлен в значение, принятое по умолчанию для соответствующего типа данных (например, элементы массива типа **bool** будут устанавливаться в *false*, а элементы массива типа **int** — в *0*). ### Инициализация массива Помимо заполнения массива элемент за элементом (как показано в предыдущем примере), можно также заполнять его с использованием специального синтаксиса инициализации объектов. Для этого необходимо перечислить включаемые в массив элементы в фигурных скобках `{ }`. Такой синтаксис удобен при создании массива известного размера, когда нужно быстро задать его начальные значения: ```cs // Синтаксис инициализации массива с использованием // ключевого слова new int[] myArr = new int[] {10,20,30,40,50}; // Синтаксис инициализации массива без использования // ключевого слова new string[] info = { "Фамилия", "Имя", "Отчество" }; // Используем ключевое слово new и желаемый размер char[] symbol = new char[4] { 'X','Y','Z','M' }; ``` Обратите внимание, что в случае применения синтаксиса с фигурными скобками размер массива указывать не требуется (как видно на примере создания переменной *myArr*), поскольку этот размер автоматически вычисляется на основе количества элементов внутри фигурных скобок. Кроме того, применять ключевое слово **new** не обязательно (как при создании массива *info*). ### Неявно типизированные массивы *Ключевое слово var* позволяет определить переменную так, чтобы лежащий в ее основе тип выводился компилятором. Аналогичным образом можно также определять неявно типизированные локальные массивы. С использованием такого подхода можно определить новую переменную массива без указания типа элементов, содержащихся в массиве. Давайте рассмотрим пример: ```cs var arr1 = new[] { 1, 2, 3 }; Console.WriteLine( "Тип массива arr1 - {0}", arr1.GetType()); var arr2 = new[] { "One", "Two", "Three" }; Console.WriteLine( "Тип массива arr2 - {0}", arr2.GetType()); Console.ReadLine(); ``` Разумеется, как и при создании массива с использованием явного синтаксиса C#, элементы, указываемые в списке инициализации массива, должны обязательно иметь один и тот же базовый тип (т.е. должны все быть **int**, **string** и т.п.). ### Определение массива объектов В большинстве случаев при определении массива тип элемента, содержащегося в массиве, указывается явно. Хотя на первый взгляд это выглядит довольно понятно, существует одна важная особенность. В основе каждого типа в системе типов .NET (в том числе фундаментальных типов данных) в конечном итоге лежит базовый класс **System.Object**. В результате получается, что в случае определения массива объектов находящиеся внутри него элементы могут представлять собой что угодно: ```cs // Объявляем и инициализируем массив объектов object[] arrByObject = { true, 10, "Привет", 13.7m }; // Выведем в консоль тип каждого члена массива foreach (object me in arrByObject) Console.WriteLine( "Тип {0} - {1}", me, me.GetType()); Console.ReadLine(); ``` ![](../img/04001.png) ### Свойство Length Реализация в C# массивов в виде объектов дает целый ряд преимуществ. Одно из них заключается в том, что с каждым массивом связано свойство **Length**, содержащее число элементов, из которых может состоять массив. Следовательно, у каждого массива имеется специальное свойство, позволяющее определить его длину. Когда запрашивается длина многомерного массива, то возвращается общее число элементов, из которых может состоять массив. Благодаря наличию у массивов свойства Length операции с массивами во многих алгоритмах становятся более простыми, а значит, и более надежными. Давайте рассмотрим пример использования свойства *Length*: ```cs int[] myArr = { 1, 2, 3, 4 }; for (int i = 0; i < myArr.Length; i++) Console.WriteLine(myArr[i]); Console.ReadLine(); ``` ## Многомерные массивы **Многомерным** называется такой массив, который содержит два или более измерения, причем доступ к каждому элементу такого массива осуществляется с помощью определенной комбинации двух или более индексов. Многомерный массив индексируется двумя и более целыми числами. ### Двумерные массивы Простейшей формой многомерного массива является двумерный массив. Местоположение любого элемента в двумерном массиве обозначается двумя индексами. Такой массив можно представить в виде таблицы, на строки которой указывает один индекс, а на столбцы — другой. Пример объявления и инициализации двумерного массива показан ниже: ```cs // Объявляем двумерный массив int[,] myArr = new int[4, 5]; Random ran = new Random(); // Инициализируем данный массив for (int i = 0; i < 4; i++) { for (int j = 0; j < 5; j++) { myArr[i, j] = ran.Next(1, 15); Console.Write("{0}\t", myArr[i, j]); } Console.WriteLine(); } ``` ![](../img/04002.png) Обратите особое внимание на способ объявления двумерного массива. Схематическое представление массива myArr показано ниже: ![](../img/04003.png) Если вам приходилось раньше программировать на С, С++ или Java, то будьте особенно внимательны, объявляя или организуя доступ к многомерным массивам в C#. В этих языках программирования размеры массива и индексы указываются в отдельных квадратных скобках, тогда как в C# они разделяются запятой. ### Массивы трех и более измерений В C# допускаются массивы трех и более измерений. Ниже приведена общая форма объявления многомерного массива: ``` тип[,...,] имя_массива = new тип[размер1, размер2, ... размеры]; ``` Ниже приведен пример программы, использующей трехмерный массив: ```cs int[,,] myArr = new int[5,5,5]; for (int i = 0; i < 5; i++) for (int j = 0; j < 5; j++) for (int k = 0; k < 5; k++) myArr[i, j, k] = i + j + k; ``` ### Инициализация многомерных массивов Для инициализации многомерного массива достаточно заключить в фигурные скобки список инициализаторов каждого его размера: ``` тип[,] имя_массива = { {val, val, val, ..., val}, {val, val, val, ..., val}, {val, val, val, ..., val} }; ``` где *val* обозначает инициализирующее значение, а каждый внутренний блок — отдельный ряд. Первое значение в каждом ряду сохраняется на первой позиции в массиве, второе значение — на второй позиции и т.д. Обратите внимание на то, что блоки инициализаторов разделяются запятыми, а после завершающей эти блоки закрывающей фигурной скобки ставится точка с запятой. Ниже в качестве примера приведена общая форма инициализации двумерного массива: ```cs int[,] MyArr = { {1,10}, {2,20}, {3,30}, {4,40} }; ``` ## Ступенчатые (зубчатые) массивы Двумерный массив можно представить в виде таблицы, в которой длина каждой строки остается неизменной по всему массиву. Но в C# можно также создавать специальный тип двумерного массива, называемый ступенчатым массивом. Ступенчатый массив представляет собой массив массивов, в котором длина каждого массива может быть разной. Следовательно, ступенчатый массив может быть использован для составления таблицы из строк разной длины. Ступенчатые массивы объявляются с помощью ряда квадратных скобок, в которых указывается их размерность. Например, для объявления двумерного ступенчатого массива служит следующая общая форма: ``` тип [][] имя массива = new тип[размер] []; ``` где размер обозначает число строк в массиве. Память для самих строк распределяется индивидуально, и поэтому длина строк может быть разной. Давайте рассмотрим пример использования ступенчатого массива: ```cs int i = 0; // Объявляем ступенчатый массив int[][] myArr = new int[4][]; myArr[0] = new int[4]; myArr[1] = new int[6]; myArr[2] = new int[3]; myArr[3] = new int[4]; // Инициализируем ступенчатый массив for (; i < 4; i++) { myArr[0][i] = i; Console.Write("{0}\t",myArr[0][i]); } Console.WriteLine(); for (i = 0; i < 6; i++) { myArr[1][i] = i; Console.Write("{0}\t", myArr[1][i]); } Console.WriteLine(); for (i = 0; i < 3; i++) { myArr[2][i] = i; Console.Write("{0}\t", myArr[2][i]); } Console.WriteLine(); for (i = 0; i < 4; i++) { myArr[3][i] = i; Console.Write("{0}\t", myArr[3][i]); } Console.ReadLine(); ``` Созданный массив в данном примере можно структурно представить в виде: ![](../img/04004.png) Теперь нетрудно понять, почему такие массивы называются ступенчатыми! После создания ступенчатого массива доступ к его элементам осуществляется по индексу, указываемому в отдельных квадратных скобках. Ступенчатые массивы находят полезное применение не во всех, а лишь в некоторых случаях. Так, если требуется очень длинный двумерный массив, который заполняется не полностью, т.е. такой массив, в котором используются не все, а лишь отдельные его элементы, то для этой цели идеально подходит ступенчатый массив. ### Применение свойства *Length* при обращении со ступенчатыми массивами Особый случай представляет применение свойства *Length* при обращении со ступенчатыми массивами. В этом случае с помощью данного свойства можно получить длину каждого массива, составляющего ступенчатый массив. Давайте рассмотрим следующий пример: ```cs int[][] myArr = new int[3][]; myArr[0] = new int[4]; myArr[1] = new int[10]; myArr[2] = new int[1]; Console.WriteLine("Общая длина всего массива: " + myArr.Length); Console.WriteLine("\nДлина первой цепи: " + myArr[0].Length); Console.WriteLine("\nДлина второй цепи: " + myArr[1].Length); Console.WriteLine("\nДлина третьей цепи: " + myArr[2].Length); Console.ReadLine(); ``` ![](../img/04005.png) Обратите особое внимание на то, как свойство *Length* используется в ступенчатом массиве *myArr*. Напомним, что двумерный ступенчатый массив представляет собой массив массивов. Следовательно, когда используется выражение *`myArr.Length`* то в нем определяется число массивов, хранящихся в массиве myArr (в данном случае — 3 массива). А для получения длины любого отдельного массива, составляющего ступенчатый массив, служит выражение *`myArr[0].Length`*. ## Класс Array Каждый создаваемый массив получает большую часть функциональности от класса **System.Array**. Общие члены этого класса позволяют работать с массивом с использованием полноценной объектной модели. Таким образом, методы и свойства, определенные в классе **Array**, можно использовать с любым массивом C#. Давайте разберем некоторые полезные особенности класса **Array**. ### Создание массивов Класс **Array** является абстрактным, поэтому создать массив с использованием какого-либо конструктора нельзя. Однако вместо применения синтаксиса C# для создания экземпляров массивов также возможно создавать их с помощью статического метода *CreateInstance()*. Это исключительно удобно, когда заранее неизвестен тип элементов массива, поскольку тип можно передать методу *CreateInstance()* в параметре как объект **Type**: ```cs // Создаем массив типа string, длиной 5 Array myArr = Array.CreateInstance(typeof(string),5); // Инициализируем первые два поля массива myArr.SetValue("Name",0); myArr.SetValue("Age",1); // Считываем данные из массива string s = (string)myArr.GetValue(1); Console.ReadLine(); ``` Обратите внимание, что для установки значений в массив служит метод *SetValue()*, а для их чтения — метод *GetValue()*. ### Копирование массивов Поскольку массивы — это ссылочные типы, присваивание переменной типа массива другой переменной создает две переменных, ссылающихся на один и тот же массив. Для копирования массивов предусмотрена реализация массивами интерфейса **ICloneable**. Метод *Clone()*, определенный в этом интерфейсе, создает неглубокую копию массива. Если элементы массива относятся к типу значений, то все они копируются, если массив содержит элементы ссылочных типов, то сами эти элементы не копируются, а копируются лишь ссылки на них. Вместо метода *Clone()* можно также применять метод *Array.Сору()*, тоже создающий поверхностную копию. Но между *Clone()* и *Сору()* есть одно важное отличие: *Clone()* создает новый массив, а *Сору()* требует наличия существующего массива той же размерности с достаточным количеством элементов. ```cs string[] arr2 = new string[5]; // Создаем массив типа string, длиной 5 Array myArr = Array.CreateInstance(typeof(string),5); // Инициализируем первые три поля массива myArr.SetValue("Name",0); myArr.SetValue("Age",1); myArr.SetValue("Adress",2); // Копируем массив с помощью метода Clone() string[] arr1 = (string[])myArr.Clone(); foreach (string s in arr1) Console.Write("\t"+s); // Копируем с помощью метода Copy() Console.WriteLine(); Array.Copy(myArr, arr2, myArr.Length); foreach (string s in arr2) Console.Write("\t"+s); Console.ReadLine(); ``` ### Сортировка и поиск В классе **Array** реализован алгоритм быстрой сортировки (**Quicksort**) элементов массива. Метод *Sort()* требует от элементов реализации интерфейса **IComparable**. Простые типы, такие как `System.String` и `System.Int32`, реализуют **IComparable**, так что можно сортировать элементы, относящиеся к этим типам. C помощью разных вариантов метода *Sort()* можно отсортировать массив полностью или в заданных пределах либо отсортировать два массива, содержащих соответствующие пары "ключ-значение". После сортировки в массиве можно осуществить эффективный поиск, используя разные варианты метода *BinarySearch()*. ```cs int[] myArr = { 4, 5, -183, 12, 34, 0, 2 ,-13 }; Console.WriteLine( "Исходный массив чисел: "); foreach (int x in myArr) Console.Write("\t{0}",x); // Реализуем сортировку массива Console.WriteLine( "\n\nОтсортированный массив:"); Array.Sort(myArr); foreach (int x in myArr) Console.Write("\t{0}",x); // Организуем поиск числа 12 Console.WriteLine("\n\nПоиск числа:"); int search = Array.BinarySearch(myArr, 12); Console.WriteLine( "Число 12 находится на {0} позиции", search+1); Console.ReadLine(); ``` ![](../img/04006.png) ## Массивы в качестве параметров Массивы могут передаваться в методы в качестве параметров, а также возвращаться из методов. Для возврата массива достаточно объявить массив как тип возврата. Наглядно это показано в следующем примере: ```cs foreach (string s in myReturn()) Console.WriteLine(s); int[] mySortArray = {4,1,2,5,3}; for (int i = 0; i <= 4; i++) mySortArray[i] = mySort(mySortArray, i); Console.WriteLine( "\nОтсортированный массив:\n"); foreach (int i in mySortArray) Console.Write("\t"+i); Console.ReadLine(); // Данный метод возвращает массив Info static Array myReturn() { string[] Info = {"Name","Family","Adress" }; return Info; } // Передача массива в метод в качестве параметра // Данный метод возвращает значение одного из индексов // отсортированного массива static int mySort(Array sortArray, int i) { Array.Sort(sortArray); return (int)sortArray.GetValue(i); } ``` Результат работы данной программы: ![](../img/04007.png) ### Структура ArraySegment Структура *`ArraySegment`* представляет сегмент массива. Эта структура может применяться, когда нужно вернуть или передать методу части массива. Вместо передачи в метод массива, смещения и счетчика в отдельных параметрах, можно передать единственный параметр *`ArraySegment`*. В этой структуре информация о сегменте (смещение и счетчик) заключена непосредственно в ее членах: ```cs int[] arr1 = { 1, 2, 3, 4, 5, 6 }; int[] arr2 = { 7, 8, 9, 10 }; var mySegmentsArray = new ArraySegment[3] { // Инициализируем сегменты массивов new ArraySegment(arr1,0,2), new ArraySegment(arr2,0,1), new ArraySegment(arr1,1,2) }; Console.WriteLine("Сумма выбранных значений равна:\n"+SumArraySegments(mySegmentsArray)); Console.ReadLine(); // Метод, вычисляющий сумму выбранных сегментов static int SumArraySegments(ArraySegment[] value) { int sum = 0; foreach (var s in value) for (int i = s.Offset; i < s.Offset + s.Count; i++) sum += s.Array[i]; return sum; } ``` Важно отметить, что сегменты массива не копируют элементы исходного массива. Вместо этого через ArraySegment можно получить доступ к исходному массиву. Если изменяются элементы сегмента, то эти изменения будут видны в исходном массиве. ## Коллекции Работать с массивами не всегда удобно. Например, массив хранит фиксированное количество объектов, однако что делать если мы заранее не знаем, сколько нам потребуется объектов? В этом случае намного удобнее применять **коллекции**. Еще один плюс коллекций состоит в том, что некоторые из них реализует стандартные структуры данных, например, стек, очередь, словарь, которые могут пригодиться для решения различных специальных задач. Большая часть классов коллекций содержится в пространствах имен `System.Collections` (простые необобщенные классы коллекций), `System.Collections.Generic` (обобщенные или типизированные классы коллекций) и `System.Collections.Specialized` (специальные классы коллекций). Также для обеспечения параллельного выполнения задач и многопоточного доступа применяются классы коллекций из пространства имен `System.Collections.Concurrent`. ### ArrayList Итак, класс **ArrayList** представляет коллекцию объектов. И если надо сохранить вместе разнотипные объекты - строки, числа и т.д., то данный класс как раз для этого подходит. **Основные методы класса:** * `int Add(object value)`: добавляет в список объект *value* * `void AddRange(ICollection col)`: добавляет в список объекты коллекции *col*, которая представляет интерфейс ICollection - интерфейс, реализуемый коллекциями. * `void Clear()`: удаляет из списка все элементы * `bool Contains(object value)`: проверяет, содержится ли в списке объект *value*. Если содержится, возвращает true, иначе возвращает false * `void CopyTo(Array array)`: копирует текущий список в массив array. * `ArrayList GetRange(int index, int count)`: возвращает новый список *ArrayList*, который содержит *count* элементов текущего списка, начиная с индекса *index* * `int IndexOf(object value)`: возвращает индекс элемента *value* * `void Insert(int index, object value)`: добавляет в список по индексу *index* объект *value* * `void InsertRange(int index, ICollection col)`: добавляет в список начиная с индекса index коллекцию ICollection * `int LastIndexOf(object value)`: возвращает индекс последнего вхождения в списке объекта value * `void Remove(object value)`: удаляет из списка объект value * `void RemoveAt(int index)`: удаляет из списка элемент по индексу index * `void RemoveRange(int index, int count)`: удаляет из списка count элементов, начиная с индекса index * `void Reverse()`: переворачивает список * `void SetRange(int index, ICollection col)`: копирует в список элементы коллекции col, начиная с индекса index * `void Sort()`: сортирует коллекцию Кроме того, с помощью свойства *Count* можно получить количество элементов в списке. Посмотрим применение класса **ArrayList** на примере. ```cs ArrayList list = new ArrayList(); list.Add(2.3); // заносим в список объект типа double list.Add(55); // заносим в список объект типа int list.AddRange(new string[] { "Hello", "world" }); // заносим в список строковый массив // перебор значений foreach (object o in list) { Console.WriteLine(o); } // удаляем первый элемент list.RemoveAt(0); // переворачиваем список list.Reverse(); // получение элемента по индексу Console.WriteLine(list[0]); // перебор значений for (int i = 0; i < list.Count; i++) { Console.WriteLine(list[i]); } Console.ReadLine(); ``` Во-первых, так как класс **ArrayList** находится в пространстве имен **System.Collections**, то подключаем его (`using System.Collections;`). Вначале создаем объект коллекции через конструктор как объект любого другого класса: `ArrayList list = new ArrayList();`. При необходимости мы могли бы так же, как и с массивами, выполнить начальную инициализацию коллекции, например, `ArrayList list = new ArrayList(){1, 2, 5, "string", 7.7};` Далее последовательно добавляем разные значения. Данный класс коллекции, как и большинство других коллекций, имеет два способа добавления: одиночного объекта через метод *Add* и набора объектов, например, массива или другой коллекции через метод *AddRange* Через цикл *foreach* мы можем пройтись по всем объектам списка. И поскольку данная коллекция хранит разнородные объекты, а не только числа или строки, то в качестве типа перебираемых объектов выбран тип object: `foreach (object o in list)` Многие коллекции, в том числе и *ArrayList*, реализуют удаление с помощью методов *Remove/RemoveAt*. В данном случае мы удаляем первый элемент, передавая в метод RemoveAt индекс удаляемого элемента. В завершении мы опять же выводим элементы коллекции на экран только уже через цикл for. В данном случае с перебором коллекций дело обстоит также, как и с массивами. А число элементов коллекции мы можем получить через свойство Count С помощью индекса мы можем получить элемент коллекции так же, как и в массивах: `object firstObj = list[0];` ### List Класс **`List`** из пространства имен `System.Collections.Generic` представляет простейший список однотипных объектов (в угловых скобках как раз записывается тип объектов). Среди его методов можно выделить следующие: * `void Add(T item)`: добавление нового элемента в конец списка * `void AddRange(ICollection collection)`: добавление в список коллекции или массива * `int BinarySearch(T item)`: бинарный поиск элемента в списке. Если элемент найден, то метод возвращает индекс этого элемента в коллекции. При этом список ***должен быть отсортирован***. * `int IndexOf(T item)`: возвращает индекс первого вхождения элемента в списке * `void Insert(int index, T item)`: вставляет элемент item в списке на позицию index * `bool Remove(T item)`: удаляет элемент item из списка, и если удаление прошло успешно, то возвращает true * `void RemoveAt(int index)`: удаление элемента по указанному индексу index * `void Sort()`: сортировка списка Посмотрим реализацию списка на примере: ```cs List numbers = new List() { 1, 2, 3, 45 }; numbers.Add(6); // добавление элемента numbers.AddRange(new int[] { 7, 8, 9 }); numbers.Insert(0, 666); // вставляем на первое место в списке число 666 numbers.RemoveAt(1); // удаляем второй элемент foreach (int i in numbers) { Console.WriteLine(i); } List people = new List(3); people.Add(new Person() { Name = "Том" }); people.Add(new Person() { Name = "Билл" }); foreach (Person p in people) { Console.WriteLine(p.Name); } Console.ReadLine(); class Person { public string Name { get; set; } } ``` Здесь у нас создаются два списка: один для объектов типа **int**, а другой - для объектов **Person**. В первом случае мы выполняем начальную инициализацию списка: `List numbers = new List() { 1, 2, 3, 45 };` Во втором случае мы используем другой конструктор, в который передаем начальную емкость списка: `List people = new List(3);`. Указание начальной емкости списка (capacity) позволяет в будущем увеличить производительность и уменьшить издержки на выделение памяти при добавлении элементов. Также начальную емкость можно установить с помощью свойства *Capacity*, которое имеется у класса **List**. ### Dictionary Еще один распространенный тип коллекции представляют словари. Словарь хранит объекты, которые представляют пару ключ-значение. Каждый такой объект является объектом структуры `KeyValuePair`. Благодаря свойствам *Key* и *Value*, которые есть у данной структуры, мы можем получить ключ и значение элемента в словаре. Рассмотрим на примере использование словарей: ```cs Dictionary countries = new Dictionary(5); countries.Add(1, "Russia"); countries.Add(3, "Great Britain"); countries.Add(2, "USA"); countries.Add(4, "France"); countries.Add(5, "China"); foreach (KeyValuePair keyValue in countries) { Console.WriteLine(keyValue.Key + " - " + keyValue.Value); } // получение элемента по ключу string country = countries[4]; // изменение объекта countries[4] = "Spain"; // удаление по ключу countries.Remove(2); ``` Класс словарей также, как и другие коллекции, предоставляет методы *Add* и *Remove* для добавления и удаления элементов. Только в случае словарей в метод *Add* передаются два параметра: ключ и значение. А метод *Remove* удаляет не по индексу, а по ключу. Так как в нашем примере ключами является объекты типа int, а значениями - объекты типа string, то словарь в нашем случае будет хранить объекты `KeyValuePair`. В цикле **foreach** мы их можем получить и извлечь из них ключ и значение. Кроме того, мы можем получить отдельно коллекции ключей и значений словаря: ```cs Dictionary people = new Dictionary(); people.Add('b', new Person() { Name = "Bill" }); people.Add('t', new Person() { Name = "Tom" }); people.Add('j', new Person() { Name = "John" }); foreach (KeyValuePair keyValue in people) { // keyValue.Value представляет класс Person Console.WriteLine(keyValue.Key + " - " + keyValue.Value.Name); } // перебор ключей foreach (char c in people.Keys) { Console.WriteLine(c); } // перебор по значениям foreach (Person p in people.Values) { Console.WriteLine(p.Name); } ``` Здесь в качестве ключей выступают объекты типа **char**, а значениями - объекты *Person*. Используя свойство **Keys**, мы можем получить ключи словаря, а свойство Values соответственно хранит все значения в словаре. Для добавления необязательно применять метод `Add()`, можно использовать сокращенный вариант: ```cs Dictionary people = new Dictionary(); people.Add('b', new Person() { Name = "Bill" }); people['a'] = new Person() { Name = "Alice" }; ``` Несмотря на то, что изначально в словаре нет ключа 'a' и соответствующего ему элемента, то он все равно будет установлен. Если же он есть, то элемент по ключу 'a' будет заменен на новый объект `new Person() { Name = "Alice" }` **Инициализация словарей** Если в C# 5.0 мы могли инициализировать словари следующим образом: ```cs Dictionary countries = new Dictionary { {"Франция", "Париж"}, {"Германия", "Берлин"}, {"Великобритания", "Лондон"} }; foreach(var pair in countries) Console.WriteLine("{0} - {1}", pair.Key, pair.Value); ``` То начиная с C# 6.0 доступен также еще один способ инициализации: ```cs Dictionary countries = new Dictionary { ["Франция"] = "Париж", ["Германия"] = "Берлин", ["Великобритания"] = "Лондон" }; ``` --- Предыдущая лекция |   | Следующая лекция :----------------:|:----------:|:----------------: [Операторы и операции языка.](./articles/t3l1_2.md) | [Содержание](../readme.md#тема-4-программирование-на-языке-c-основы) | [Строки.](./articles/4_prog_string.md)