Массивы Все типы переменных, которые мы рассматривали до сих пор, обладают одним общим свойством: в них хранится единственное значение (или — в случае струк- тур — единственный набор значений). Но в ситуациях, когда необходимо хранить большое количество однородных данных, такой подход может оказаться не совсем удобным. В таких случаях более приемлемым было бы одновременное хранение нескольких значений одного типа без использования отдельной переменной для каждого значения. Предположим, к примеру, что вам необходимо выполнить вычисления, в кото- рых задействованы имена всех ваших друзей. Для этого можно воспользоваться обычными переменными типа string, например, так: Дополнительные сведения о переменных 93 string friendNamel = «Robert Barwell"; string friendName2 = "Mike Parry"; string friendName3 - "Jeremy Beacock"; Однако в этом случае вам потребуется затратить много усилий, особенно по мере написания кода для обработки каждой переменной в отдельности. Отсутству- ет и возможность пройти по всему этому списку строк в цикле. Альтернативной возможностью является использование массивов. Массив представляет собой список индексированных переменных. Допустим, что у нас имеется массив, в котором хранятся все три имени, приведенные выше, и который называется friendNames. Тогда мы получаем доступ к отдельным элементам этого массива, просто указывая их индекс в квадратных скобках, как показано в следую- щем примере: friendNames[<индекс>] Индекс — это целое число, имеющее значение 0 для первого вхождения, 1 — для второго и т. д. Это означает, что у нас появляется возможность пройти все вхождения в цикле, например: int i; for (i = 0; i < 3; i++) { . : /::-: ;: •: :. ;• ,-• I Console. WriteLine ("Имя с индексом {0}: {!)", i, friendNames [I].) ; } Каждый массив обладает единым базовым типом, т. е. все отдельные вхожде- ния в массив имеют один и тот же тип. В данном случае массив friendNames имеет базовый тип string, поскольку он предназначается для хранения переменных типа string. Вхождения массива часто называются элементами. Объявление массивов Массивы объявляются следующим образом: <базовыйТип>[] <имя>; В данном случае <базовыйтип> может быть произвольным типом, в том числе перечислимым или структурой. Прежде чем получить доступ к массиву, его необходимо инициализировать. Но получить доступ или присвоить значение элементу массива следующим образом нельзя: int[] myIntArray; myIntArray[10] = 5; Массивы могут быть инициализированы двумя способами. Мы можем либо задать полное содержимое массива в литеральной форме, либо указать размер массива и использовать для инициализации всех его элементов ключевое слово new (НОВЫЙ). Определение значений элементов массива в литеральной форме предполагает задание списка значений, разделенных запятыми и заключенных в фигурные скоб- ки. Например: i n t [ ] mylntArray = {5, 9, 10, 2, 99}; Q4 ^_ Глава 5 В данном случае описывается массив, состоящий их пяти элементов, и всем им присваивается целое значение. < Второй способ предполагает использование следующего синтаксиса: int [ ] myIntArray = new int [ 5 ] ; Здесь используется ключевое слово new для явной инициализации массива и константа для определения его размера. При этом методе всем вхождениям мас- сива будет присвоено значение по умолчанию, которое дая численных типов рав- но 0. Для инициализации массивов вместо констант с тем же успехом можно использовать переменные, например: int[] mylntArray = new int [размерМассива] ; При желании можно одновременно использовать оба метода: i n t [ ] mylntArray = new i n t [5] {5, 9, 10, 2 , 99}; В этом случае размеры массива должны совпадать с количеством элементов в спи- ске. Следующая запись является недопустимой: i n t [ ] m y l n t A r r a y | new i n t [ 1 0 ] { 5 , 9 , 1 0 , 2 , 9 9 } ; Здесь описывается массив, состоящий из 10 элементов, а определяются только 5 из них; компиляция этой записи не пройдет. Побочным эффектом такого положе- ния вещей является то, что если мы описываем размерность массива с помощью переменной, то она должна являться константой. Например: ,-. ;-д-:--::: • : int[] mylntArray ~ new int [array Size] {5, 9, 10, 2, 99}; Если ключевое слово const будет пропущено, программа работать не будет. Как и в случае переменных других типов, нет никакой необходимости инициа- лизировать массив в той же строке ~в которой он объявляется. Вполне допустимой является следующая запись: int[] myIntArray; mylntArray = new int[5]; Теперь вы уже знаете достаточно для того, чтобы попробовать написать неко- торую программу. Практикум: использование массивов 1. Создайте новое консольное приложение с именем спО5ЕхО4 В директории C:\BegCSharp\Chapter5. 2. Добавьте следующий код в ciassi.cs: static void Main (string [] args) { string[] friendNames = ("Robert Barwell", "Mike Parry", "Jeremy Beacock*}; int i; friendNames.Length); for (i = 0; i < friendNames.Length; i++) ; i . ' . . \Ш:: . . Console.WriteLine(friendNames[i]); ;' • i i i l i i l P Дополнительные сведения о переменных 95 [Не ре are 3 of ад friends« 'Robert Barbell Mike Panvy Jerero*? Beacock Press ant* hey to continue j 3. Запустите программу (см. рис. справа). Как это работает В этом коде объявляется мас- сив ТИПа s t r i n g , СОСТОЯЩИЙ ИЗ трех значений, которые затем выводятся на консоль с помощью цикла for. Обратите внимание, что существует возможность узнать общее количество элементов массива посредством параметра friendNames.Length: Console.WriteLineCHere are {0} of my friends:*; friendNames.Length); Это очень удобный способ определения размера массива. При выводе значений массива в цикле for легко допустить ошибку. Например, попытаемся вместо оператора < использовать оператор < = , как показано ниже: (for 0; i <= friendNames.Length; i++) Console.WriteLine(friendNames[i]); [Microsoft Visual Studio Vzbumzr Break Continue I Компиляциях последующим выполне- нием программы приведет к появлению окна с сообщением об ошибке: "Необра- батываемая исключительная ситуация типа 'выход индекса за пределы диапазо- на' В ChO5ExO4.exe" (СМ. рИС. СЛева). В приведенном случае мы попытались ПОЛУЧИТЬ ДОСТуп К friendNames[3]. Как Щ вы помните, значения индексов массива начинаются с нуля, поэтому последним элементом данного массива является friendNames [2]. Обращение к элементам массива, выходящим за его границу, приведет к ошибке. На самом деле существует более удобный способ доступа ке всем элементам Массива С ПОМОЩЬЮ ЦИКЛа foreach. Циклы foreach Цикл foreach позволяет обращаться ко всем элементам массива с помощью очень простого синтаксиса: foreach (<базовыйТип> <имя> in <массив>) •;{;; // можно использовать <имя> для доступа к очередному элементу Этот цикл пройдет по всем элементам массива, последовательно помещая каждый из них в переменную <имя>; при этом опасность выхода за границы массива отсут- ствует. Нам не придется беспокоиться о том, сколько элементов имеется в массиве, и мы сможем воспользоваться в цикле каждым из них. Для того чтобы применить этот способ, изменим код последнего примера: static void Main(string[] args) { string[] friendNames = {"Robert Harwell", "Mike Parry", "Jeremy Beacock"}; 96 _ Глава 5 Console.WriteLine(*Here are {0} of my friends:*, friendNames.Length); { Console.WriteLine(friendName) : \ o T : •••••• : ' : : ' . : • } • .. \. . ' •••.••••• ; ; - 4 : y • • • . ; . ; : • : № • / : " : - Ш | } Выходной поток этой программы будет абсолютно таким же, как и в предыдущем примере. Основное отличие этого метода от обычного цикла for заключается в том, что он позволяет использовать содержимое массива в режиме "только чтение", т. е. отсутствует возможность изменять значения элементов массива. Например, напи- сать такую программу нельзя: foreach (string friendName in friendNames) { friendName = "Rupert the bear"; } При попытке откомпилировать этот код будет выдано сообщение об ошибке. Многомерные массивы Прочитав заглавие этого раздела, вы, возможно, подумаете, что мы собираемся обсуждать какое-то научно-фантастическое приложение к языку С#. На самом же деле многомерный массив — это объект, в котором для осуществления доступа к его элементам используется несколько индексов. В качестве примера представим себе случай, когда требуется отразить высоту возвышенности по измеряемым точкам на местности. Мы можем определить такие точки двумя координатами х и у и использовать эти координаты в качестве индек- сов так, чтобы в массиве с названием hiiiHeight хранилась высота холма, соот- ветствующая точке с данной парой координат. Для этого потребуется использовать многомерный массив. Двумерный массив такого вида объявляется следующим образом: <бпЭовыйТш1>[,) <имя>; Описания массивов с большим числом размерностей требуют больше запятых. Например: <базовыйТип>[,,,] <имя>; Так объявляется четырехмерный массив. Для присваивания значений используется аналогичный синтаксис, причем раз- мерности отделяются одна от другой запятыми. Для того чтобы объявить и иници- ализировать обсуждавшийся выше двумерный массив hiiiHeight с базовым типом double, размерностью х, равной 3, и размерностью у, равной 4, необходима следу- ющая строка: : :••: double [,] hiiiHeight = new double [3,4] ; В качестве альтернативы при начальном присваивании значений могут исполь- зоваться литеральные значения. Для этого воспользуемся вложенными блоками, взятыми в фигурные скобки и разделенными запятыми. Например: d o u b l e [ J h i i i H e i g h t = new {{1, 2, 3, 4 } , {2, 3, 4, 5 } , {3, 4, 5, 6}}; Этот массив имеет точно такие же описания размерностей, что и предыдущий, однако в данном случае значения элементов массива задаются явно. Дополнительные сведения о переменных 97 hilHeight[O,O] hillHeight[1,0] hil!Height[2,0] hillHeight[Of 1] hillHeight[0f2] hillHeight[0,3] hillHeight[1,1] hillHeight[1,2] hillHeight[1,3] hillHeight[2,1] hillHeight[2,2] hillHeight[2,3] 6 Для того чтобы получить доступ к отдельным элементам многомерного массива, следует указать индексы этого элемента, разделенные запятыми. Например: hillHeight [2 Д] Теперь над этим элементом можно выполнять действия так же, как и над другими элементами. Это выражение позволяет получить доступ ко второму элементу третьего вло- женного массива в соответствии с вышеприведенным описанием (в данном случае значение равняется 4). Запомните, что отсчет начинается с 0 и что первая цифра относится к вложенному массиву. Другими словами, первая цифра определяет но- мер пары фигурных скобок, а вторая цифра определяет соответствующий элемент внутри этой пары фигурных скобок. Визуально такой массив выглядит, как показано на рисунке слева. Цикл foreach позволяет осуществлять до- ступ ко всем элементам многомерных масси- вов так же, как и в случае одномерных. Например: d o u b l e t , ] h i l l H e i g h t = { { 1 , 2 , 3 , 4 } , { 2 , 3 , 4 , 5 } , { 3 , 4 , 5 , 6 } } ; f o r e a c h ' d o u b l e h e i g h t i n h i l l H e i g h t ) . '••.••':•.'-:.". ;. .: /: ',:-V J,.'- ,'•:•:: 1111111 ' : ЩШ1 ; : -• :•"•"••• :-:' Элементы этого массива будут выводиться в том же порядке, в котором проис- ходило присваивание им литеральных значений: hillHeight[0,0] hillHeight[0,l] hillHeight[0,2] hillHeight[0,3] hillHeight[1,0] hillHeight[1,1] hillHeight[1,2] И Т.Д. Массивы массивов Многомерные массивы, обсуждавшиеся в предшествующем разделе, обычно называются прямоугольными, поскольку у них каждая "строка" имеет один и тот же размер. Если использовать наш пример, то координата у может изменяться от 0 до 2 для любой координаты х. Однако существует возможность использовать ступенчатые (jagged) массивы, у которых "строки" могут быть неодинакового размера. Для этого требуется мас- сив, каждым элементом которого также является массив. При желании можно использовать массивы массивов массивов и даже более сложные конструкции. Од- нако это оказывается возможным только в том случае, если все массивы имеют один и тот же базовый тип. Синтаксис, применяемый для объявления массивов массивов, подразумевает ис- пользование в объявлении массива нескольких пар квадратных скобок, например: int[][] j aggedlntArray; gg ~ Глава 5 К сожалению, инициализация подобных массивов оказывается не таким простым делом, как инициализация многомерных массивов. Так, например, мы не имеем возможности использовать вслед за этим объявлением следующую запись: jaggedlntArray = new i n t [ 3 ] [ 4 ] ; Но даже в том случае, если бы мы могли применять запись такого вида, вряд ли от этого была бы какая-нибудь польза, поскольку в случае многомерных массивов точно такого же эффекта можно достичь с меньшими затратами. Следующий код также является недопустимым: jaggedlnt Array = {{1, 2, 3), {1}, {1, 2}}; На самом деле существуют две возможности. Можно сначала инициализиро- вать массив, содержащий другие массивы (чтобы избежать путаницы, мы будем называть вложенные массивы субмассивами), а затем, в свою очередь, инициали- зировать эти субмассивы: jaggedlntArray - new int[2][]; jaggedlntArray [0] = new int[3]; jaggedlnt Array [1] = new int[4]; Можно также воспользоваться видоизмененной формой приведенного выше литерального присваивания: jaggedlntArray = {new int[] {1, 2, 3}, new int[] {1}, new int[] {1,2}}; Для таких неоднородных массивов можно использовать циклы foreach, однако чаще всего вложенные циклы применяются для того, чтобы получить доступ к реальным данным. Например, допустим, что имеется следующий неоднородный массив, кото- рый включает в себя десять других массивов, каждый из которых содержит целые значения, являющиеся делителями целых чисел из диапазона от 1 до 10: int[] [] divisorslTolO = {new int[] {1}, new int[] {1,2}, new int[] {1, 3}, new int[] {1, 2, 4}, ; new int [] {1, 5}, new int[] {1, 2, 3, 6}, new int[] {1,7}, new int [] {1, 2, 4, 8}, new int [ ] {1, 3 , 9} r new int[] {1, 2, 5, 1С}}; Использование следующего кода: foreach (int divisor in divisorslTolO) v / •;:' v . . / . : : ; . { ' •' ; : • ../v ".;• •' Console.WriteLine(divisor); •.•••• ^ • } является недопустимым, поскольку divisorslTolO содержит в себе не элементы int, а элементы int [ ]. Поэтому нам придется проходить не только по самому массиву, но и непосредственно по каждому субмассиву: foreach (int[] divisorsOfIntin divisorslTolO) foreach (int divisor in divisorsOfInt) : • ' ' - • • • ' { ' У " " " : ; . : / . . . .: • . ' Console.WriteLine(divisor); Дополнительные сведения о переменных ' 99 Как вы, вероятно, заметили, при использовании неоднородных массивов синтак- сис очень быстро становится сложным! В большинстве случаев оказывается проще использовать либо прямоугольные массивы, либо какой-нибудь другой, более про- стой способ хранения данных. Однако могут возникать ситуации, в которых при- дется использовать именно этот метод, а дополнительные знания не повредят!
|