Введение в программирование на C# 2.0

Массивы

Разбить на страницы
Показывать лекцию целиком

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

Массив. Объявление

Массив – множество однотипных элементов. Это тоже ТИП. Любой массив наследует класс (является производным от класса – о принципе наследования позже) System.Array.

Существует несколько способов создания группировок однотипных объектов:

  • объявление множества однотипных элементов в рамках перечисления (класса, структуры),
  • определение собственно массива.
  • Принципиальная разница состоит в следующем:

  • доступ к данным - членам перечисления, класса производится ПО имени данного члена (элементы перечисления, класса или структуры ИНОГДА могут быть одного типа, но каждый член всегда имеет собственное имя),
  • элементы массива ВСЕГДА однотипны, располагаются в непрерывной области памяти,
  • доступ к элементу массива осуществляется по индексу, при этом допускается случайный доступ.
  • Одной из характеристик массива является ранг или размерность массива. Массив размерности (или ранга) N ( N определяет число измерений массива) – это Массив массивов (или составляющих массива) ранга N–1. Составляющие массива – это массивы меньшей размерности, являющиеся элементами данного массива. При этом составляющая массива сама может быть либо массивом, либо элементом массива:

    ОбъявлениеМассива ::=
    ИмяТипа СписокСпецификаторовРазмерности ИмяМассива [ИнициализацияМассива]; 
    ИмяТипа ::= Идентификатор
    ИмяМассива ::= Идентификатор
    
    СписокСпецификаторовРазмерности
    ::= [СписокСпецификаторовРазмерности] СпецификаторРазмерности
    ::= [СписокНеявныхСпецификаторов]
    СпецификаторРазмерности ::= []
    СписокНеявныхСпецификаторов
    СписокНеявныхСпецификаторов ::= [СписокНеявныхСпецификаторов ,] НеявныйСпецификатор
    НеявныйСпецификатор ::= ПУСТО | РАЗДЕЛИТЕЛЬ

    ПУСТО – оно и есть пусто.

    РАЗДЕЛИТЕЛЬ – пробел, несколько пробелов, символ табуляции, символ перехода на новую строку, комбинация символов "новая строка/возврат каретки" и прочая икебана...

    При объявлении массива действуют следующие правила:

  • Спецификатор размерности, состоящий из одного неявного спецификатора [], специфицирует составляющую массива размерности 1.
  • Спецификатор размерности, состоящий из N неявных спецификаторов [ ,,, ... ,], специфицирует составляющую массива размерности N.
  • Длина списка спецификаторов размерности массива не ограничена.
  • При этом информация о типе составляющих массива в объявлении массива определяется на основе типа массива и списка его спецификаторов размерности.

    Синтаксис объявления массива (ссылки на массив) позволяет специфицировать массивы произвольной конфигурации без какого-либо намека на количественные характеристики составляющих массивы элементов.

    Ниже представлены способы ОБЪЯВЛЕНИЯ ссылок на массивы РАЗЛИЧНОЙ размерности и конфигурации:

    // Объявлены ссылки на массивы размерности 3 элементов типа int.
    // Это массивы составляющих, представляющих собой массивы элементов
    // размерности 2 одномерных массивов элементов типа int.
    // Размеры всех составляющих массивов одного уровня равны
    // между собой (так называемые "прямоугольные" массивы). 
    int[,,] arr0; 
    int[	,	,	] arr1; 
    int[
    ,
    ,
    ] arr2; 
    // Объявлена ссылка на 
    // ОДНОМЕРНЫЙ(!) массив 
    // ОДНОМЕРНЫХ(!) элементов массива, каждый из которых является 
    // ОДНОМЕРНЫМ(!) массивом элементов типа int.
    int[][][] arr3;
    // Объявлена ссылка на 
    // ОДНОМЕРНЫЙ(!) массив составляющих, каждая из которых является 
    // ДВУМЕРНЫМ(!) массивом массивов элементов типа int.
    // При этом никаких ограничений на размеры "прямоугольных" составляющих
    // данное объявление не содержит. У всех составляющих могут быть разные
    // размеры.
    int[][,] arr4;
    // Объявлена ссылка на 
    // ДВУМЕРНЫЙ(!) массив составляющих, каждая из которых является 
    // ОДНОМЕРНЫМ(!) массивом элементов типа int.
    // При этом никаких ограничений на размеры одномерных составляющих
    // данное объявление не содержит. У всех составляющих могут быть разные
    // размеры.
    int[,][] arr5;

    Рассмотренный синтаксис объявления и инициализации массива позволяет определять ДВЕ различных категории массивов:

  • простые (прямоугольные) массивы,
  • jagged (вложенные) массивы.
  • Инициализация массивов

    Массив мало объявить, его нужно еще проинициализировать. Только тогда ранее специфицированное множество однотипных объектов будет размещено в памяти.

    Определение предполагает спецификацию КОЛИЧЕСТВА объектов по каждому измерению.

    ИнициализацияМассива ::= = newИнициализация
    ::= = ИнициализацияСписком
    ::= = ЗаполняющаяИнициализация
    ::= = ИнициализацияКопированием
    
    newИнициализация ::= new ИмяТипа СписокОписателейРазмерности
    ЗаполняющаяИнициализация ::= newИнициализация ИнициализацияСписком
    
    СписокОписателейРазмерности ::= [СписокОписателейРазмерности] ОписательРазмерности
    ОписательРазмерности ::= [СписокОписателей] | ПустойОписатель
    СписокОписателей ::= [СписокОписателей ,] Описатель 
    Описатель ::= Выражение
    ПустойОписатель ::= [ПУСТО]

    Выражение-описатель должно быть:

  • целочисленным,
  • с определенным значением.
  • В управляемой памяти нет ничего, что бы создавалось без конструктора, – даже если его присутствие не обозначено явным образом:

    string s = "qwerty";
    int[] intArray = {1,2,3,4,5};

    Эти операторы являются всего лишь сокращенной формой следующих операторов определения:

    string s = new string(new char[]{'q','w','e','r','t','y'});
    int[] intArray = new int[5]{1,2,3,4,5};

    К моменту создания массива должна быть определена его основная характеристика – количество составляющих данный массив элементов. Такой фокус при определении и инициализации массивов не проходит:

    int x; // К моменту определения массива значение x должно быть определено!
    int[] arr0 = new int[x];

    Так нормально! В момент определения массива CLR знает ВСЕ НЕОБХОДИМЫЕ характеристики определяемого массива:

    int x = 10;
    int[][] arr1 = new int[x][125];
    // Внимание! Для jagged такое определение недопустимо!
    // Нет никаких ограничений  на размеры составляющих массивов.
    // Для каждой составляющей требуется ЯВНАЯ спецификация.

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

    Количество составляющих массив элементов — это не всегда ВСЕ образующие массив элементы!

    При определении такой конструкции, как МАССИВ МАССИВОВ, инициализирующая запись должна содержать ПОЛНУЮ информацию о характеристиках первой составляющей массива, то есть об общем количестве составляющих данный массив элементов массивов. Все остальные описатели могут оставаться пустыми.

    int val1 = 100;
    // На этом этапе определения массива массивов
    // элементов типа int принципиально знание характеристик первой составляющей!
    // Все остальные могут быть заданы после!
    int [][] x0 = new int[15][];
    int [][][] x1 = new int[val1][][];
    int [,][] x2 = new int[val1,7][];
    int [,,][,][] x3 = new int[2,val1,7][,][];
    
    // Следующие способы объявления корректными не являются.
    // int [][][] y0 = new int[val1][2][];
    // int [][][] y1 = new int[][2][7];
    // int [,][] y2 = new int[ ,2][7];
    // int [,,] y3 = new int[val1,2, ];

    При такой форме определения массива предполагается многоступенчатая инициализация, при которой производится последовательная инициализация составляющих массива:

    ИнициализацияСписком ::= {СписокВыражений}
    ::= {СписокИнициализаторовСоставляющихМассива}
    СписокИнициализаторовСоставляющихМассива ::=
    [СписокИнициализаторовСоставляющихМассива , ] ИнициализаторМассива
    ИнициализаторМассива ::= newИнициализация {СписокВыражений} | {СписокВыражений}
    СписокВыражений ::= [СписокВыражений ,] Выражение

    При инициализации списком каждый элемент массива (или составляющая массива) получает собственное значение из списка:

    int[] r = {val1, 1, 4, 1*val1};

    Избыточная инициализация с дополнительной возможностью уточнения характеристик массива:

    int[] r = new int[] {val1, 1, 4, 1*val1};
    int[] t = new int[4] {val1, 1, 4, 1*val1};

    В первом случае избыточная инициализация – рецидив многословия. Тип значений элементов массива определяется спецификатором массива, количество элементов массива определяется количеством элементов списка выражений.

    Во втором случае избыточная инициализация – способ дополнительного контроля размеров массива. Транслятор сосчитает количество элементов списка и сопоставит его со значением описателя.

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

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

    ИнициализацияКопированием ::= СпецификацияСоставляющей
    СпецификацияСоставляющей ::= ИмяМассива СписокИндексныхВыражений
    СписокИндексныхВыражений ::= [СписокИндексныхВыражений] [Индексное Выражение]
    ИндексноеВыражение ::= Выражение

    В силу того, что массив является объектом ссылочного типа, составляющие одного массива могут быть использованы для инициализации другого массива. Ответственность за возможные переплетения ссылок возлагается на программиста:

    int [] q = {0,1,2,3,4,5,6,7,8,9};
    int [][] d1 = {
    new int[3],
    new int[5]
    	 };
    int [][][] d2 = new int[2][][];
    d2[0] = new int[50][]; d2[0][0] = d1[0];
    // d2[0][0] ссылается на составляющую массива d1.
    d2[0][1] = new int[125]; 
    d2[1] = new int[50][]; 
    d2[1][1] = new int[10]{1,2,3,4,5,6,7,8,9,10}; 
    d2[1][0] = q;
    // d2[1][0] ссылается на ранее объявленный и определенный массив q.

    В следующем примере одномерный массив myArray строится из элементов типа int.

    int[] myArray = new int [5];

    Операция new используется для создания массива и инициализации его элементов предопределенными значениями. В результате выполнения этого оператора все элементы массива будут установлены в ноль.

    Простой строковый массив можно объявить и проинициализировать аналогичным образом:

    string[] myStringArray = new string[6];

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

    int[] myArray = new int[] {1, 3, 5, 7, 9};

    Строковый массив может быть проинициализирован аналогичным образом:

    string[] weekDays = new string[] {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};

    И это не единственный способ объявления и инициализации.

    Если объявление совмещается с инициализацией, операция new может быть опущена. Предполагается, что транслятор знает, ЧТО при этом нужно делать:

    string[] weekDays = {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};

    Объявление и инициализация вообще могут быть размещены в разных операторах. Но в этом случае без операции new ничего не получится:

    int[] myArray;
    myArray = new int[] {1, 3, 5, 7, 9}; // Так можно.
    myArray = {1, 3, 5, 7, 9}; // Так нельзя.

    А вот объявление одномерного массива, состоящего из трех элементов, каждый из которых является одномерным массивом целых:

    int[][] myJaggedArray = new int[3][];

    Дальнейшее использование этого массива требует инициализации его элементов. Например, так:

    myJaggedArray[0] = new int[5];
    myJaggedArray[1] = new int[4];
    myJaggedArray[2] = new int[2];

    Каждый из элементов является одномерным массивом целых. Количество элементов каждого массива очевидно из соответствующих операторов определения.

    Ниже показан пример использования заполняющей инициализации, при которой одновременно с определением (созданием) массивов производится присвоение элементам новорожденных массивов конкретных значений:

    myJaggedArray[0] = new int[] {1,3,5,7,9};
    myJaggedArray[1] = new int[] {0,2,4,6};
    myJaggedArray[2] = new int[] {11,22};

    Вышеупомянутый массив может быть объявлен и проинициализирован и таким образом:

    int[][] myJaggedArray = new int [][]
     {
    new int[] {1,3,5,7,9},
    new int[] {0,2,4,6},
    new int[] {11,22}
     };

    И еще один эквивалентный способ инициализации массива. В этом случае используется неявная инициализация на верхнем уровне, как при инициализации обычного одномерного массива. Важно (!), что при определении составляющих этого массива операция new опущена быть не может. Каждая компонента требует явного применения операции new или присвоения ссылки на ранее созданный одномерный массив:

    int[][] myJaggedArray = {
    new int[] {1,3,5,7,9},
    new int[] {0,2,4,6},
    new int[] {11,22}
     };

    Доступ к элементам ступенчатого массива обеспечивается посредством выражений индексации:

    // Assign 33 to the second element of the first array:
    myJaggedArray[0][1] = 33;
    // Assign 44 to the second element of the third array:
    myJaggedArray[2][1] = 44;

    C# позволяет собирать разнообразные конструкции на основе jagged многомерных массивов. Ниже приводится пример объявления и инициализации одномерного jagged-массива, содержащего в качестве элементов двумерные массивы различных размеров:

    int[][,] myJaggedArray = new int [3][,]
     {
    new int[,] { {1,3}, {5,7} },
    new int[,] { {0,2}, {4,6}, {8,10} },
    new int[,] { {11,22}, {99,88}, {0,9} } 
     };

    Доступ к отдельным элементам jagged-массива обеспечивается различными комбинациями выражений индексации. В приводимом ниже примере выводится значение элемента массива [1,0], расположенного по нулевому индексу myJaggedArray (это 5 ):

    Console.Write("{0}", myJaggedArray[0][1,0]);

    И еще один пример, где строится массив myArray, элементами которого являются массивы. Каждый из составляющих имеет собственные размеры.

    using System;
    public class ArrayTest 
    {
    public static void Main() 
     {
    int[][] myArray = new int[2][];
    myArray[0] = new int[5] {1,3,5,7,9};
    myArray[1] = new int[4] {2,4,6,8};
    
    for (int i=0; i < myArray.Length; i++) 
     {
    Console.Write("Element({0}):", i);
    for (int j = 0 ; j < myArray[i].Length ; j++)
    Console.Write("{0}{1}",myArray[i][j], j == (myArray[i].Length–1) 
    ? "" : " ");
    Console.WriteLine();
     }
     }
    }

    Результат:

    Element(0): 1 3 5 7 9
    Element(1): 2 4 6 8

    Value Type и Reference Type. Два типа массивов

    Рассмотрим следующее объявление:

    MyType[] myArray = new MyType[10];

    Результат выполнения этого оператора зависит от того, что собой представляет тип MyType.

    Возможны всего два варианта: MyType может быть типом-значением или типом-ссылкой.

    Если это тип-значение, результатом выполнения оператора будет массив, содержащий 10 объектов MyType с предопределенными значениями.

    Если MyType является ссылочным типом, то в результате выполнения данного оператора будет создан массив из 10 элементов типа "ссылка", каждый из которых будет проинициализирован пустой ссылкой – значением null.

    Доступ к элементам массива реализуется в соответствии с правилом индексации – по каждому измерению индексация осуществляется с НУЛЯ до n–1, где n – количество элементов размерности.

    Встроенный сервис по обслуживанию простых массивов

    При работе с массивами следует иметь в виду одно важное обстоятельство.

    В .NET ВСЕ массивы происходят от ОДНОГО общего (базового) класса Array. Это означает, что ВСЕ созданные в программе массивы обеспечиваются специальным набором методов для создания, управления, поиска и сортировки элементов массива. К числу таких методов и свойств, в частности, относятся следующие свойства:

    public int Length {get;}

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

    public int Rank {get;}

    Возвращает целое число, представляющее количество измерений массива.

    И методы:

    public static Array CreateInstance(Type, int, int);

    Статический метод (один из вариантов), создает массив элементов заданного типа и определенной размерности:

    public int GetLength (int dimension);

    Возвращает количество элементов заданной параметром размерности:

    public void SetValue(object, int, int);

    Присваивает элементу массива значение, представленное первым параметром (один из вариантов):

    public object GetValue(int, int);

    Извлекает значение из двумерного массива по индексам (один из вариантов):

    using System;
    public class DemoArray  
    {
    
    public static void Main()  
    {
    // Создали и проинициализировали двумерный массив строк.
    Array myArray=Array.CreateInstance( typeof(String), 2, 4 );
    myArray.SetValue( "Вышел ", 0, 0 );
    myArray.SetValue( "Месяц, ", 0, 1 );
    myArray.SetValue( "из", 0, 2 );
    myArray.SetValue( "тумана,", 0, 3 );
    myArray.SetValue( "вынул", 1, 0 );
    myArray.SetValue( "ножик", 1, 1 );
    myArray.SetValue( "из", 1, 2 );
    myArray.SetValue( "кармана.", 1, 3 );
    
    // Показали содержимое массива.
    Console.WriteLine( "The Array contains the following values:" );
    for ( int i = myArray.GetLowerBound(0); i <= myArray.GetUpperBound(0); i++ )
    for ( int j = myArray.GetLowerBound(1); j <= myArray.GetUpperBound(1); j++ )
    Console.WriteLine( "\t[{0},{1}]:\t{2}", i, j, myArray.GetValue( i, j ) );
    }
    }

    Элементы массива массивов (jagged array) на одном и том же уровне могут иметь разные размеры. На этих массивах функция GetLength() и свойство Rank показывают результаты, аналогичные простым одномерным массивам.

    Массивы как параметры

    В качестве параметра методу всегда можно передать ссылку на массив — в частности, ссылку на ОДНОМЕРНЫЙ массив, первая спецификация размерности которого в объявлении имеет вид ...[] ... .

    Тип и количество составляющих данный массив компонентов для механизма передачи параметров значения не имеют. Важно, что в стеке будет выделено определенное (соответствующее значению первой размерности) количество проинициализированных ссылок на составляющие данный одномерный массив элементы.

    Этот принцип действует во всех случаях, в том числе и при вызове приложения из командной строки с передачей ему в качестве параметров массива ОДНОТИПНЫХ (строковых) значений:

    using System;
    class C1
    {
    static void Main(string[] args)
     {
    Console.WriteLine("Values of parameters:");
    for (int i = 0; i < args.Length; i++)
     {
    Console.WriteLine("{0}: {1}", i, args[i]);
     } 
     }
    }

    Сначала создается консольное приложение (пусть под именем ConLine, в файле ConLine.cs ).

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

    C:> csc /t:exe ConLine.cs

    Результат деятельности транслятора размещается в .exe-файле под именем ConLine.exe и также может быть запущен из командной строки с передачей строк символов в качестве входных параметров:

    C:>ConLine qwerty asdfgh zxcvbn ok ok ok–k

    В окне консольного приложения отобразится следующее множество строк:

    Values of parameters:
    0: qwerty
    1: asdfgh
    2: zxcvbn
    3: ok
    4: ok
    5: ok–k

    А теперь с использованием оператора foreach:

    using System;
    class C1
    {
    static void Main(string[] args)
     {
    Console.WriteLine("Values of parameters, using foreach:");
    foreach (string arg in args)
     {
    Console.WriteLine("{0}", arg);
     } 
     }
    }

    Запускаем:

    C:>ConLine qwerty asdfgh zxcvbn ok ok ok–k

    Получаем:

    Values of parameters, using foreach:
    qwerty
    asdfgh
    zxcvbn
    ok
    ok
    ok–k

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

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

    Например:

    int[] myArray; // myArray == null
    PrintArray(myArray);

    Передаваемый в качестве параметра массив может быть предварительно проинициализирован:

    // cs_sd_arrays.cs
    using System;
    public class ArrayClass 
    {
    static void PrintArray(string[] w) 
     {
    for (int i = 0 ; i < w.Length ; i++)
     { 
    Console.Write(w[i] + "{0}", i < w.Length – 1 ? " " : "");
     }
    
    Console.WriteLine();
     }
    
    public static void Main() 
     {
    // Declare and initialize an array:
    string[] WeekDays = new string []
    {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};
    // Pass the array as a parameter:
    PrintArray(WeekDays);
     }
    }

    Еще один пример совмещения инициализации массива с его передачей как параметра методу. Перебор элементов массива реализуется в соответствии с правилом индексации:

    using System;
    public class ArrayClass 
    {
    static void PrintArray(int[,] w) 
     {
    // Display the array elements:
    for (int i=0; i < 4; i++) 
    for (int j=0; j < 2; j++)
    Console.WriteLine("Element({0},{1})={2}", i, j, w[i,j]);
     }
    
    public static void Main() 
     {
    // Pass the array as a parameter:
    PrintArray(new int[,] {{1,2}, {3,4}, {5,6}, {7,8}});
     }
    }

    Спецификатор params

    Неопределенное (переменное) количество (ОДНОТИПНЫХ!) параметров или список параметров переменной длины передается в функцию в виде ссылки на одномерный массив. Эта ссылка в списке параметров функции должна быть последним элементом списка параметров. Ссылке должен предшествовать спецификатор params.

    В выражении вызова метода со списком параметров, члены списка могут присутствовать либо в виде списка однотипных значений (этот список преобразуется в массив значений), либо в виде ссылки на ОДНОМЕРНЫЙ массив значений определенного типа:

    using System;
    
    class Class1
    {
    
    // Методы, принимающие списки параметров переменной длины.
    // Их объявление. Единственный параметр способен принимать
    // массивы значений переменной длины.
    static void f1(params int[] m)
    {
    Console.WriteLine(m.Length.ToString());
    }
    
    static void f2(params int[][,] p)
    {
    Console.WriteLine(p.Length.ToString());
    }
    
    
    static void Main(string[] args)
    {
    // Выражения вызова. На основе списков однотипных
    // значений формируются массивы.  
    f1(0,1,2,3,4,5);	
    f1(0,1,2,3,4,5,6,7,8,9,10);	
    
    f2(new int[,]{{0,1},{2,3}}, new int[,]{{0,1,2,3,4,5},{6,7,8,9,10,11}});
    f2(new int [,]{{0,1},{2,3}});
    }
    }
    Страницы:

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

    Массив. Объявление

    Массив – множество однотипных элементов. Это тоже ТИП. Любой массив наследует класс (является производным от класса – о принципе наследования позже) System.Array.

    Существует несколько способов создания группировок однотипных объектов:

  • объявление множества однотипных элементов в рамках перечисления (класса, структуры),
  • определение собственно массива.
  • Принципиальная разница состоит в следующем:

  • доступ к данным - членам перечисления, класса производится ПО имени данного члена (элементы перечисления, класса или структуры ИНОГДА могут быть одного типа, но каждый член всегда имеет собственное имя),
  • элементы массива ВСЕГДА однотипны, располагаются в непрерывной области памяти,
  • доступ к элементу массива осуществляется по индексу, при этом допускается случайный доступ.
  • Одной из характеристик массива является ранг или размерность массива. Массив размерности (или ранга) N ( N определяет число измерений массива) – это Массив массивов (или составляющих массива) ранга N–1. Составляющие массива – это массивы меньшей размерности, являющиеся элементами данного массива. При этом составляющая массива сама может быть либо массивом, либо элементом массива:

    ОбъявлениеМассива ::=
    ИмяТипа СписокСпецификаторовРазмерности ИмяМассива [ИнициализацияМассива]; 
    ИмяТипа ::= Идентификатор
    ИмяМассива ::= Идентификатор
    
    СписокСпецификаторовРазмерности
    ::= [СписокСпецификаторовРазмерности] СпецификаторРазмерности
    ::= [СписокНеявныхСпецификаторов]
    СпецификаторРазмерности ::= []
    СписокНеявныхСпецификаторов
    СписокНеявныхСпецификаторов ::= [СписокНеявныхСпецификаторов ,] НеявныйСпецификатор
    НеявныйСпецификатор ::= ПУСТО | РАЗДЕЛИТЕЛЬ

    ПУСТО – оно и есть пусто.

    РАЗДЕЛИТЕЛЬ – пробел, несколько пробелов, символ табуляции, символ перехода на новую строку, комбинация символов "новая строка/возврат каретки" и прочая икебана...

    При объявлении массива действуют следующие правила:

  • Спецификатор размерности, состоящий из одного неявного спецификатора [], специфицирует составляющую массива размерности 1.
  • Спецификатор размерности, состоящий из N неявных спецификаторов [ ,,, ... ,], специфицирует составляющую массива размерности N.
  • Длина списка спецификаторов размерности массива не ограничена.
  • При этом информация о типе составляющих массива в объявлении массива определяется на основе типа массива и списка его спецификаторов размерности.

    Синтаксис объявления массива (ссылки на массив) позволяет специфицировать массивы произвольной конфигурации без какого-либо намека на количественные характеристики составляющих массивы элементов.

    Ниже представлены способы ОБЪЯВЛЕНИЯ ссылок на массивы РАЗЛИЧНОЙ размерности и конфигурации:

    // Объявлены ссылки на массивы размерности 3 элементов типа int.
    // Это массивы составляющих, представляющих собой массивы элементов
    // размерности 2 одномерных массивов элементов типа int.
    // Размеры всех составляющих массивов одного уровня равны
    // между собой (так называемые "прямоугольные" массивы). 
    int[,,] arr0; 
    int[	,	,	] arr1; 
    int[
    ,
    ,
    ] arr2; 
    // Объявлена ссылка на 
    // ОДНОМЕРНЫЙ(!) массив 
    // ОДНОМЕРНЫХ(!) элементов массива, каждый из которых является 
    // ОДНОМЕРНЫМ(!) массивом элементов типа int.
    int[][][] arr3;
    // Объявлена ссылка на 
    // ОДНОМЕРНЫЙ(!) массив составляющих, каждая из которых является 
    // ДВУМЕРНЫМ(!) массивом массивов элементов типа int.
    // При этом никаких ограничений на размеры "прямоугольных" составляющих
    // данное объявление не содержит. У всех составляющих могут быть разные
    // размеры.
    int[][,] arr4;
    // Объявлена ссылка на 
    // ДВУМЕРНЫЙ(!) массив составляющих, каждая из которых является 
    // ОДНОМЕРНЫМ(!) массивом элементов типа int.
    // При этом никаких ограничений на размеры одномерных составляющих
    // данное объявление не содержит. У всех составляющих могут быть разные
    // размеры.
    int[,][] arr5;

    Рассмотренный синтаксис объявления и инициализации массива позволяет определять ДВЕ различных категории массивов:

  • простые (прямоугольные) массивы,
  • jagged (вложенные) массивы.
  • Инициализация массивов

    Массив мало объявить, его нужно еще проинициализировать. Только тогда ранее специфицированное множество однотипных объектов будет размещено в памяти.

    Определение предполагает спецификацию КОЛИЧЕСТВА объектов по каждому измерению.

    ИнициализацияМассива ::= = newИнициализация
    ::= = ИнициализацияСписком
    ::= = ЗаполняющаяИнициализация
    ::= = ИнициализацияКопированием
    
    newИнициализация ::= new ИмяТипа СписокОписателейРазмерности
    ЗаполняющаяИнициализация ::= newИнициализация ИнициализацияСписком
    
    СписокОписателейРазмерности ::= [СписокОписателейРазмерности] ОписательРазмерности
    ОписательРазмерности ::= [СписокОписателей] | ПустойОписатель
    СписокОписателей ::= [СписокОписателей ,] Описатель 
    Описатель ::= Выражение
    ПустойОписатель ::= [ПУСТО]

    Выражение-описатель должно быть:

  • целочисленным,
  • с определенным значением.
  • В управляемой памяти нет ничего, что бы создавалось без конструктора, – даже если его присутствие не обозначено явным образом:

    string s = "qwerty";
    int[] intArray = {1,2,3,4,5};

    Эти операторы являются всего лишь сокращенной формой следующих операторов определения:

    string s = new string(new char[]{'q','w','e','r','t','y'});
    int[] intArray = new int[5]{1,2,3,4,5};

    К моменту создания массива должна быть определена его основная характеристика – количество составляющих данный массив элементов. Такой фокус при определении и инициализации массивов не проходит:

    int x; // К моменту определения массива значение x должно быть определено!
    int[] arr0 = new int[x];

    Так нормально! В момент определения массива CLR знает ВСЕ НЕОБХОДИМЫЕ характеристики определяемого массива:

    int x = 10;
    int[][] arr1 = new int[x][125];
    // Внимание! Для jagged такое определение недопустимо!
    // Нет никаких ограничений  на размеры составляющих массивов.
    // Для каждой составляющей требуется ЯВНАЯ спецификация.

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

    Количество составляющих массив элементов — это не всегда ВСЕ образующие массив элементы!

    При определении такой конструкции, как МАССИВ МАССИВОВ, инициализирующая запись должна содержать ПОЛНУЮ информацию о характеристиках первой составляющей массива, то есть об общем количестве составляющих данный массив элементов массивов. Все остальные описатели могут оставаться пустыми.

    int val1 = 100;
    // На этом этапе определения массива массивов
    // элементов типа int принципиально знание характеристик первой составляющей!
    // Все остальные могут быть заданы после!
    int [][] x0 = new int[15][];
    int [][][] x1 = new int[val1][][];
    int [,][] x2 = new int[val1,7][];
    int [,,][,][] x3 = new int[2,val1,7][,][];
    
    // Следующие способы объявления корректными не являются.
    // int [][][] y0 = new int[val1][2][];
    // int [][][] y1 = new int[][2][7];
    // int [,][] y2 = new int[ ,2][7];
    // int [,,] y3 = new int[val1,2, ];

    При такой форме определения массива предполагается многоступенчатая инициализация, при которой производится последовательная инициализация составляющих массива:

    ИнициализацияСписком ::= {СписокВыражений}
    ::= {СписокИнициализаторовСоставляющихМассива}
    СписокИнициализаторовСоставляющихМассива ::=
    [СписокИнициализаторовСоставляющихМассива , ] ИнициализаторМассива
    ИнициализаторМассива ::= newИнициализация {СписокВыражений} | {СписокВыражений}
    СписокВыражений ::= [СписокВыражений ,] Выражение

    При инициализации списком каждый элемент массива (или составляющая массива) получает собственное значение из списка:

    int[] r = {val1, 1, 4, 1*val1};

    Избыточная инициализация с дополнительной возможностью уточнения характеристик массива:

    int[] r = new int[] {val1, 1, 4, 1*val1};
    int[] t = new int[4] {val1, 1, 4, 1*val1};

    В первом случае избыточная инициализация – рецидив многословия. Тип значений элементов массива определяется спецификатором массива, количество элементов массива определяется количеством элементов списка выражений.

    Во втором случае избыточная инициализация – способ дополнительного контроля размеров массива. Транслятор сосчитает количество элементов списка и сопоставит его со значением описателя.

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

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

    ИнициализацияКопированием ::= СпецификацияСоставляющей
    СпецификацияСоставляющей ::= ИмяМассива СписокИндексныхВыражений
    СписокИндексныхВыражений ::= [СписокИндексныхВыражений] [Индексное Выражение]
    ИндексноеВыражение ::= Выражение

    В силу того, что массив является объектом ссылочного типа, составляющие одного массива могут быть использованы для инициализации другого массива. Ответственность за возможные переплетения ссылок возлагается на программиста:

    int [] q = {0,1,2,3,4,5,6,7,8,9};
    int [][] d1 = {
    new int[3],
    new int[5]
    	 };
    int [][][] d2 = new int[2][][];
    d2[0] = new int[50][]; d2[0][0] = d1[0];
    // d2[0][0] ссылается на составляющую массива d1.
    d2[0][1] = new int[125]; 
    d2[1] = new int[50][]; 
    d2[1][1] = new int[10]{1,2,3,4,5,6,7,8,9,10}; 
    d2[1][0] = q;
    // d2[1][0] ссылается на ранее объявленный и определенный массив q.

    В следующем примере одномерный массив myArray строится из элементов типа int.

    int[] myArray = new int [5];

    Операция new используется для создания массива и инициализации его элементов предопределенными значениями. В результате выполнения этого оператора все элементы массива будут установлены в ноль.

    Простой строковый массив можно объявить и проинициализировать аналогичным образом:

    string[] myStringArray = new string[6];

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

    int[] myArray = new int[] {1, 3, 5, 7, 9};

    Строковый массив может быть проинициализирован аналогичным образом:

    string[] weekDays = new string[] {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};

    И это не единственный способ объявления и инициализации.

    Если объявление совмещается с инициализацией, операция new может быть опущена. Предполагается, что транслятор знает, ЧТО при этом нужно делать:

    string[] weekDays = {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};

    Объявление и инициализация вообще могут быть размещены в разных операторах. Но в этом случае без операции new ничего не получится:

    int[] myArray;
    myArray = new int[] {1, 3, 5, 7, 9}; // Так можно.
    myArray = {1, 3, 5, 7, 9}; // Так нельзя.

    А вот объявление одномерного массива, состоящего из трех элементов, каждый из которых является одномерным массивом целых:

    int[][] myJaggedArray = new int[3][];

    Дальнейшее использование этого массива требует инициализации его элементов. Например, так:

    myJaggedArray[0] = new int[5];
    myJaggedArray[1] = new int[4];
    myJaggedArray[2] = new int[2];

    Каждый из элементов является одномерным массивом целых. Количество элементов каждого массива очевидно из соответствующих операторов определения.

    Ниже показан пример использования заполняющей инициализации, при которой одновременно с определением (созданием) массивов производится присвоение элементам новорожденных массивов конкретных значений:

    myJaggedArray[0] = new int[] {1,3,5,7,9};
    myJaggedArray[1] = new int[] {0,2,4,6};
    myJaggedArray[2] = new int[] {11,22};

    Вышеупомянутый массив может быть объявлен и проинициализирован и таким образом:

    int[][] myJaggedArray = new int [][]
     {
    new int[] {1,3,5,7,9},
    new int[] {0,2,4,6},
    new int[] {11,22}
     };

    И еще один эквивалентный способ инициализации массива. В этом случае используется неявная инициализация на верхнем уровне, как при инициализации обычного одномерного массива. Важно (!), что при определении составляющих этого массива операция new опущена быть не может. Каждая компонента требует явного применения операции new или присвоения ссылки на ранее созданный одномерный массив:

    int[][] myJaggedArray = {
    new int[] {1,3,5,7,9},
    new int[] {0,2,4,6},
    new int[] {11,22}
     };

    Доступ к элементам ступенчатого массива обеспечивается посредством выражений индексации:

    // Assign 33 to the second element of the first array:
    myJaggedArray[0][1] = 33;
    // Assign 44 to the second element of the third array:
    myJaggedArray[2][1] = 44;

    C# позволяет собирать разнообразные конструкции на основе jagged многомерных массивов. Ниже приводится пример объявления и инициализации одномерного jagged-массива, содержащего в качестве элементов двумерные массивы различных размеров:

    int[][,] myJaggedArray = new int [3][,]
     {
    new int[,] { {1,3}, {5,7} },
    new int[,] { {0,2}, {4,6}, {8,10} },
    new int[,] { {11,22}, {99,88}, {0,9} } 
     };

    Доступ к отдельным элементам jagged-массива обеспечивается различными комбинациями выражений индексации. В приводимом ниже примере выводится значение элемента массива [1,0], расположенного по нулевому индексу myJaggedArray (это 5 ):

    Console.Write("{0}", myJaggedArray[0][1,0]);

    И еще один пример, где строится массив myArray, элементами которого являются массивы. Каждый из составляющих имеет собственные размеры.

    using System;
    public class ArrayTest 
    {
    public static void Main() 
     {
    int[][] myArray = new int[2][];
    myArray[0] = new int[5] {1,3,5,7,9};
    myArray[1] = new int[4] {2,4,6,8};
    
    for (int i=0; i < myArray.Length; i++) 
     {
    Console.Write("Element({0}):", i);
    for (int j = 0 ; j < myArray[i].Length ; j++)
    Console.Write("{0}{1}",myArray[i][j], j == (myArray[i].Length–1) 
    ? "" : " ");
    Console.WriteLine();
     }
     }
    }

    Результат:

    Element(0): 1 3 5 7 9
    Element(1): 2 4 6 8

    Value Type и Reference Type. Два типа массивов

    Рассмотрим следующее объявление:

    MyType[] myArray = new MyType[10];

    Результат выполнения этого оператора зависит от того, что собой представляет тип MyType.

    Возможны всего два варианта: MyType может быть типом-значением или типом-ссылкой.

    Если это тип-значение, результатом выполнения оператора будет массив, содержащий 10 объектов MyType с предопределенными значениями.

    Если MyType является ссылочным типом, то в результате выполнения данного оператора будет создан массив из 10 элементов типа "ссылка", каждый из которых будет проинициализирован пустой ссылкой – значением null.

    Доступ к элементам массива реализуется в соответствии с правилом индексации – по каждому измерению индексация осуществляется с НУЛЯ до n–1, где n – количество элементов размерности.

    Встроенный сервис по обслуживанию простых массивов

    При работе с массивами следует иметь в виду одно важное обстоятельство.

    В .NET ВСЕ массивы происходят от ОДНОГО общего (базового) класса Array. Это означает, что ВСЕ созданные в программе массивы обеспечиваются специальным набором методов для создания, управления, поиска и сортировки элементов массива. К числу таких методов и свойств, в частности, относятся следующие свойства:

    public int Length {get;}

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

    public int Rank {get;}

    Возвращает целое число, представляющее количество измерений массива.

    И методы:

    public static Array CreateInstance(Type, int, int);

    Статический метод (один из вариантов), создает массив элементов заданного типа и определенной размерности:

    public int GetLength (int dimension);

    Возвращает количество элементов заданной параметром размерности:

    public void SetValue(object, int, int);

    Присваивает элементу массива значение, представленное первым параметром (один из вариантов):

    public object GetValue(int, int);

    Извлекает значение из двумерного массива по индексам (один из вариантов):

    using System;
    public class DemoArray  
    {
    
    public static void Main()  
    {
    // Создали и проинициализировали двумерный массив строк.
    Array myArray=Array.CreateInstance( typeof(String), 2, 4 );
    myArray.SetValue( "Вышел ", 0, 0 );
    myArray.SetValue( "Месяц, ", 0, 1 );
    myArray.SetValue( "из", 0, 2 );
    myArray.SetValue( "тумана,", 0, 3 );
    myArray.SetValue( "вынул", 1, 0 );
    myArray.SetValue( "ножик", 1, 1 );
    myArray.SetValue( "из", 1, 2 );
    myArray.SetValue( "кармана.", 1, 3 );
    
    // Показали содержимое массива.
    Console.WriteLine( "The Array contains the following values:" );
    for ( int i = myArray.GetLowerBound(0); i <= myArray.GetUpperBound(0); i++ )
    for ( int j = myArray.GetLowerBound(1); j <= myArray.GetUpperBound(1); j++ )
    Console.WriteLine( "\t[{0},{1}]:\t{2}", i, j, myArray.GetValue( i, j ) );
    }
    }

    Элементы массива массивов (jagged array) на одном и том же уровне могут иметь разные размеры. На этих массивах функция GetLength() и свойство Rank показывают результаты, аналогичные простым одномерным массивам.

    Массивы как параметры

    В качестве параметра методу всегда можно передать ссылку на массив — в частности, ссылку на ОДНОМЕРНЫЙ массив, первая спецификация размерности которого в объявлении имеет вид ...[] ... .

    Тип и количество составляющих данный массив компонентов для механизма передачи параметров значения не имеют. Важно, что в стеке будет выделено определенное (соответствующее значению первой размерности) количество проинициализированных ссылок на составляющие данный одномерный массив элементы.

    Этот принцип действует во всех случаях, в том числе и при вызове приложения из командной строки с передачей ему в качестве параметров массива ОДНОТИПНЫХ (строковых) значений:

    using System;
    class C1
    {
    static void Main(string[] args)
     {
    Console.WriteLine("Values of parameters:");
    for (int i = 0; i < args.Length; i++)
     {
    Console.WriteLine("{0}: {1}", i, args[i]);
     } 
     }
    }

    Сначала создается консольное приложение (пусть под именем ConLine, в файле ConLine.cs ).

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

    C:> csc /t:exe ConLine.cs

    Результат деятельности транслятора размещается в .exe-файле под именем ConLine.exe и также может быть запущен из командной строки с передачей строк символов в качестве входных параметров:

    C:>ConLine qwerty asdfgh zxcvbn ok ok ok–k

    В окне консольного приложения отобразится следующее множество строк:

    Values of parameters:
    0: qwerty
    1: asdfgh
    2: zxcvbn
    3: ok
    4: ok
    5: ok–k

    А теперь с использованием оператора foreach:

    using System;
    class C1
    {
    static void Main(string[] args)
     {
    Console.WriteLine("Values of parameters, using foreach:");
    foreach (string arg in args)
     {
    Console.WriteLine("{0}", arg);
     } 
     }
    }

    Запускаем:

    C:>ConLine qwerty asdfgh zxcvbn ok ok ok–k

    Получаем:

    Values of parameters, using foreach:
    qwerty
    asdfgh
    zxcvbn
    ok
    ok
    ok–k

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

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

    Например:

    int[] myArray; // myArray == null
    PrintArray(myArray);

    Передаваемый в качестве параметра массив может быть предварительно проинициализирован:

    // cs_sd_arrays.cs
    using System;
    public class ArrayClass 
    {
    static void PrintArray(string[] w) 
     {
    for (int i = 0 ; i < w.Length ; i++)
     { 
    Console.Write(w[i] + "{0}", i < w.Length – 1 ? " " : "");
     }
    
    Console.WriteLine();
     }
    
    public static void Main() 
     {
    // Declare and initialize an array:
    string[] WeekDays = new string []
    {"Sun","Sat","Mon","Tue","Wed","Thu","Fri"};
    // Pass the array as a parameter:
    PrintArray(WeekDays);
     }
    }

    Еще один пример совмещения инициализации массива с его передачей как параметра методу. Перебор элементов массива реализуется в соответствии с правилом индексации:

    using System;
    public class ArrayClass 
    {
    static void PrintArray(int[,] w) 
     {
    // Display the array elements:
    for (int i=0; i < 4; i++) 
    for (int j=0; j < 2; j++)
    Console.WriteLine("Element({0},{1})={2}", i, j, w[i,j]);
     }
    
    public static void Main() 
     {
    // Pass the array as a parameter:
    PrintArray(new int[,] {{1,2}, {3,4}, {5,6}, {7,8}});
     }
    }

    Спецификатор params

    Неопределенное (переменное) количество (ОДНОТИПНЫХ!) параметров или список параметров переменной длины передается в функцию в виде ссылки на одномерный массив. Эта ссылка в списке параметров функции должна быть последним элементом списка параметров. Ссылке должен предшествовать спецификатор params.

    В выражении вызова метода со списком параметров, члены списка могут присутствовать либо в виде списка однотипных значений (этот список преобразуется в массив значений), либо в виде ссылки на ОДНОМЕРНЫЙ массив значений определенного типа:

    using System;
    
    class Class1
    {
    
    // Методы, принимающие списки параметров переменной длины.
    // Их объявление. Единственный параметр способен принимать
    // массивы значений переменной длины.
    static void f1(params int[] m)
    {
    Console.WriteLine(m.Length.ToString());
    }
    
    static void f2(params int[][,] p)
    {
    Console.WriteLine(p.Length.ToString());
    }
    
    
    static void Main(string[] args)
    {
    // Выражения вызова. На основе списков однотипных
    // значений формируются массивы.  
    f1(0,1,2,3,4,5);	
    f1(0,1,2,3,4,5,6,7,8,9,10);	
    
    f2(new int[,]{{0,1},{2,3}}, new int[,]{{0,1,2,3,4,5},{6,7,8,9,10,11}});
    f2(new int [,]{{0,1},{2,3}});
    }
    }
    Вернуться к учебному плану