17 массивов
17.1 Общие
Массив — это структура данных, содержащая несколько переменных, доступ к которым осуществляется по вычисляемым индексам. Содержащиеся в массиве переменные именуются элементами этого массива. Все они имеют одинаковый тип, который называется типом элементов массива.
Массив имеет ранг, определяющий количество индексов, связанных с каждым элементом массива. Ранг массива также называется измерениями массива. Массив с рангом одного называется одномерным массивом. Массив с рангом больше одного называется многомерным массивом. Многомерные массивы определенного размера часто называются двумерными массивами, трехмерные массивы и т. д. Каждое измерение массива имеет связанную длину, которая является целочисленным числом, превышающим или равным нулю. Длина измерения не является частью типа массива, а устанавливается при создании экземпляра типа массива во время выполнения. Длина измерения определяет допустимый диапазон индексов для этого измерения: для измерения длины N
индексы могут варьироваться N – 1
от 0
до инклюзивного. Общее количество элементов в массиве — это произведение длины каждого измерения в массиве. Если один или несколько измерений массива имеют длину нулю, массив считается пустым.
Тип элемента массива может быть типом массива (§17.2.1). Такие массивы массивов отличаются от многомерных массивов и могут использоваться для представления "многомерных массивов".
Пример:
int[][] pascals = { new int[] {1}, new int[] {1, 1}, new int[] {1, 2, 1}, new int[] {1, 3, 3, 1} };
пример конца
Каждый тип массива является ссылочным типом (§8.2). Тип элемента массива может быть любым типом, включая типы значений и типы массивов.
Типы массивов 17.2
17.2.1 Общие
Грамматические типы массивов предоставляются в §8.2.1.
Тип массива записывается как non_array_type за которым следует один или несколько rank_specifier.
Non_array_type — это любой тип, который не является array_type.
Ранг типа массива присваивается самым левым rank_specifier в array_type: rank_specifier указывает, что массив является массивом с рангом одного плюс число маркеров ",
" в rank_specifier.
Тип элемента типа массива — это тип, который приводит к удалению самого левого rank_specifier:
- Тип массива формы
T[R]
— это массив с рангомR
и типомT
элемента, отличного от массива. - Тип массива формы
T[R][R₁]...[Rₓ]
— это массив с рангомR
и типомT[R₁]...[Rₓ]
элемента.
В действительности rank_specifierсчитываются слева направо до конечного типа элемента, отличного от массива.
Пример: тип является
T[][,,][,]
одномерным массивом трехмерных массивов двухмерных массивовint
. пример конца
Во время выполнения значение типа массива может быть null
или ссылкой на экземпляр этого типа массива.
Примечание. В соответствии с правилами §17.6 значение также может быть ссылкой на ковариантный тип массива. конечная заметка
17.2.2 Тип System.Array
System.Array
Тип является абстрактным базовым типом всех типов массивов. Неявное преобразование ссылок (§10.2.8) существует из любого типа массива в System.Array
любой тип интерфейса и любого типа интерфейса, реализованного с помощью System.Array
. Явное преобразование ссылок (§10.3.5) существует из System.Array
любого типа интерфейса, реализуемого любым типом System.Array
массива. System.Array
не является самой array_type. Скорее, это class_type , от которого производны все array_type.
Во время выполнения значение типа System.Array
может быть null
или ссылка на экземпляр любого типа массива.
Массивы 17.2.3 и универсальные интерфейсы коллекции
Одномерный массив T[]
реализует интерфейс System.Collections.Generic.IList<T>
(IList<T>
для короткого) и его базовые интерфейсы. Соответственно, происходит неявное преобразование из T[]
IList<T>
и его базовых интерфейсов. Кроме того, если есть неявное преобразование ссылок, S
оттуда S[]
реализуется IList<T>
и происходит неявное преобразование ссылок из IList<T>
S[]
и его базовых интерфейсов (§10.2.8).T
Если есть явное преобразование S
ссылок на то есть явное преобразование ссылок из IList<T>
S[]
и его базовых интерфейсов (§10.3.5).T
Аналогичным образом одномерный массив T[]
также реализует интерфейс System.Collections.Generic.IReadOnlyList<T>
(IReadOnlyList<T>
для краткого) и его базовых интерфейсов. Соответственно, происходит неявное преобразование из T[]
IReadOnlyList<T>
и его базовых интерфейсов. Кроме того, если есть неявное преобразование ссылок, S
оттуда S[]
реализуется IReadOnlyList<T>
и происходит неявное преобразование ссылок из IReadOnlyList<T>
S[]
и его базовых интерфейсов (§10.2.8).T
Если есть явное преобразование S
ссылок на то есть явное преобразование ссылок из IReadOnlyList<T>
S[]
и его базовых интерфейсов (§10.3.5).T
Пример: например:
class Test { static void Main() { string[] sa = new string[5]; object[] oa1 = new object[5]; object[] oa2 = sa; IList<string> lst1 = sa; // Ok IList<string> lst2 = oa1; // Error, cast needed IList<object> lst3 = sa; // Ok IList<object> lst4 = oa1; // Ok IList<string> lst5 = (IList<string>)oa1; // Exception IList<string> lst6 = (IList<string>)oa2; // Ok IReadOnlyList<string> lst7 = sa; // Ok IReadOnlyList<string> lst8 = oa1; // Error, cast needed IReadOnlyList<object> lst9 = sa; // Ok IReadOnlyList<object> lst10 = oa1; // Ok IReadOnlyList<string> lst11 = (IReadOnlyList<string>)oa1; // Exception IReadOnlyList<string> lst12 = (IReadOnlyList<string>)oa2; // Ok } }
Назначение
lst2 = oa1
создает ошибку во время компиляции, так как преобразование изobject[]
IList<string>
неявного преобразования является явным преобразованием. Приведение(IList<string>)oa1
приведет к возникновению исключения во время выполнения, так какoa1
ссылки на нееobject[]
string[]
и не являются. Однако приведение (IList<string>)oa2
не приведет к возникновению исключения, так какoa2
ссылки на нееstring[]
возникают.пример конца
Всякий раз, когда происходит неявное или явное преобразование ссылок в S[]
IList<T>
, также имеется явное преобразование ссылок из IList<T>
и его базовых интерфейсов S[]
в (§10.3.5).
При реализации IList<T>
типа S[]
массива некоторые члены реализованного интерфейса могут вызывать исключения. Точное поведение реализации интерфейса выходит за рамки этой спецификации.
Создание массива 17.3
Экземпляры массива создаются array_creation_expression(§12.8.16.5) или по объявлениям полей или локальных переменных, которые включают array_initializer (§17.7). Экземпляры массива также можно создавать неявно в рамках оценки списка аргументов, включающего массив параметров (§15.6.2.4).
При создании экземпляра массива устанавливается ранг и длина каждого измерения, а затем остается константой в течение всего времени существования экземпляра. Другими словами, невозможно изменить ранг существующего экземпляра массива и изменить его размеры.
Экземпляр массива всегда имеет тип массива. Тип System.Array
является абстрактным типом, который не может быть создан.
Элементы массивов, созданных array_creation_expressions, всегда инициализированы в значение по умолчанию (§9.3).
Доступ к элементу массива 17.4
К элементам массива обращаются с помощью выражений element_access (§12.8.11.2) формыA[I₁, I₂, ..., Iₓ]
, где A
выражение типа массива и каждое Iₑ
из них является выражением типа int
, uint
long
или ulong
может быть неявно преобразовано в один или несколько этих типов. Результатом доступа к элементу массива является переменная, а именно элемент массива, выбранный индексами.
Элементы массива можно перечислить с помощью foreach
инструкции (§13.9.5).
Элементы массива 17.5
Каждый тип массива наследует элементы, объявленные типом System.Array
.
Ковариация массива 17.6
Для всех двух reference_typeи B
A
, если неявное преобразование ссылок (§10.2.8) или явное преобразование ссылок (§10.3.5) существует от A
B
, то такое же преобразование ссылок также существует из типа массива в тип A[R]
B[R]
массива, где R
любой заданный rank_specifier (но одинаковый для обоих типов массивов). Эта связь называется ковариантностью массива. В частности, ковариация массива означает, что значение типа массива может быть ссылкой на экземпляр типа A[R]
B[R]
массива, если неявное преобразование ссылок существует от B
A
.
Из-за ковариации массива назначения для элементов массивов ссылочных типов включают проверку времени выполнения, которая гарантирует, что значение, назначенное элементу массива, фактически является разрешенным типом (§12.21.2).
Пример:
class Test { static void Fill(object[] array, int index, int count, object value) { for (int i = index; i < index + count; i++) { array[i] = value; } } static void Main() { string[] strings = new string[100]; Fill(strings, 0, 100, "Undefined"); Fill(strings, 0, 10, null); Fill(strings, 90, 10, 0); } }
Назначение
array[i]
вFill
методе неявно включает проверку во время выполнения, которая гарантирует, чтоnull
value
это ссылка или ссылка на объект типа, совместимого с фактическим типомarray
элемента. ВMain
, первые два вызоваFill
успешно, но третий вызов вызываетSystem.ArrayTypeMismatchException
исключение при выполнении первого заданияarray[i]
. Исключение возникает, так как прямоугольныйint
не может храниться в массивеstring
.пример конца
Ковариация массива специально не распространяется на массивы value_types. Например, преобразование не существует, позволяющее int[]
рассматривать его как .object[]
Инициализаторы массива 17.7
Инициализаторы массива могут быть указаны в объявлениях полей (§15.5), объявлениях локальных переменных (§13.6.2) и выражениях создания массива (§12.8.16.5):
array_initializer
: '{' variable_initializer_list? '}'
| '{' variable_initializer_list ',' '}'
;
variable_initializer_list
: variable_initializer (',' variable_initializer)*
;
variable_initializer
: expression
| array_initializer
;
Инициализатор массива состоит из последовательности инициализаторов переменных, заключенных в маркеры "{
" и "}
" и разделенных,
"" маркерами. Каждый инициализатор переменных является выражением или в случае многомерного массива инициализатором вложенного массива.
Контекст, в котором используется инициализатор массива, определяет тип инициализированного массива. В выражении создания массива тип массива сразу же предшествует инициализатору или выводится из выражений в инициализаторе массива. В объявлении поля или переменной тип массива является типом объявленного поля или переменной. Когда инициализатор массива используется в объявлении поля или переменной,
int[] a = {0, 2, 4, 6, 8};
Это просто сокращено для эквивалентного выражения создания массива:
int[] a = new int[] {0, 2, 4, 6, 8};
Для одномерного массива инициализатор массива должен состоять из последовательности выражений, каждый из которых имеет неявное преобразование в тип элемента массива (§10.2). Выражения инициализируют элементы массива в растущем порядке, начиная с элемента с нуля индекса. Количество выражений в инициализаторе массива определяет длину создаваемого экземпляра массива.
Пример. Инициализатор массива, приведенный выше, создает
int[]
экземпляр длиной 5, а затем инициализирует экземпляр со следующими значениями:a[0] = 0; a[1] = 2; a[2] = 4; a[3] = 6; a[4] = 8;
пример конца
Для многомерного массива инициализатор массива должен иметь столько уровней вложения, сколько размеров в массиве. Самый внешний уровень вложения соответствует самому левому измерению, а самый внутренний уровень вложения соответствует самому правому измерению. Длина каждого измерения массива определяется числом элементов на соответствующем уровне вложения в инициализаторе массива. Для каждого инициализатора вложенного массива число элементов должно совпадать с другими инициализаторами массива на том же уровне.
Пример: пример:
int[,] b = {{0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}};
создает двухмерный массив длиной 5 для самого левого измерения и длиной двух для самого правого измерения:
int[,] b = new int[5, 2];
а затем инициализирует экземпляр массива со следующими значениями:
b[0, 0] = 0; b[0, 1] = 1; b[1, 0] = 2; b[1, 1] = 3; b[2, 0] = 4; b[2, 1] = 5; b[3, 0] = 6; b[3, 1] = 7; b[4, 0] = 8; b[4, 1] = 9;
пример конца
Если измерение, отличное от самого правого, присваивается ноль длины, то последующие измерения также имеют нуль длины.
Пример:
int[,] c = {};
создает двухмерный массив с длиной нуля как для самого левого, так и для самого правого измерения:
int[,] c = new int[0, 0];
пример конца
Если выражение создания массива включает как явные длины измерений, так и инициализатор массива, длина должна быть константными выражениями, а количество элементов на каждом уровне вложения должно соответствовать соответствующей длине измерения.
Пример. Ниже приведены некоторые примеры.
int i = 3; int[] x = new int[3] {0, 1, 2}; // OK int[] y = new int[i] {0, 1, 2}; // Error, i not a constant int[] z = new int[3] {0, 1, 2, 3}; // Error, length/initializer mismatch
Здесь инициализатор для
y
результатов ошибки во время компиляции, так как выражение длины измерения не является константой, и инициализатор дляz
результатов в результате ошибки во время компиляции, так как длина и количество элементов в инициализаторе не согласны.пример конца
Примечание. C# позволяет запятую в конце array_initializer. Этот синтаксис обеспечивает гибкость при добавлении или удалении элементов из такого списка и упрощении создания таких списков компьютеров. конечная заметка
ECMA C# draft specification