Из этого кода должно быть видно, что теперь не существует причины, чтобы каждая строка содержала одинаковое число элементов (хотя это вполне может быть, как в данном примере). В качестве примера неровного массива в C++, который имеет различное число элементов в каждой строке, можно написать:
int pMyCppArray = new int[3];
for (int i=0; i<3; i++) pMyCppArray[i] = new int[2*i + 2];
Соответствующие строки этого массива имеют размерности 2, 4 и 6. C# делает те же самые вещи почти таким же образом, хотя в случае C# синтаксис указывает число размерностей более явно:
int[][] MyJaggedArray = new int[3][];
for (int i = 0; i < 3, i++) MyJaggedArray[i] = new int[2*i + 2];
Доступ к членам неровного массива следует точно тому же синтаксису, что и в C++.
int X = MyJaggedArray[1][3];
Здесь показан неровный массив ранга 2. Однако так же, как и в C++, можно определить неровный массив с любым рангом, необходимо просто добавить прямоугольные скобки в его определение.
Проверка границ
Одной из областей, где объектная сущность массивов C# становится явной, является проверка границ. Если обратиться к элементу массива C#, указывая индекс, который не находится в границах массива, то это будет обнаружено во время выполнения и породит исключение IndexOutOfBoundsException
. В C++ этого не происходит, в результате появляются трудноуловимые ошибки. C# выполняет дополнительную проверку ошибок за счет производительности. Хотя можно было бы ожидать, что это создаст потерю производительности, на самом деле здесь содержится преимущество, так как среда выполнения .NET способна контролировать код, чтобы гарантировать, что он является безопасным в том смысле, что не будет пытаться обратиться к памяти, которая не выделена для его переменных. Это обеспечивает выигрыш производительности, так как различные приложения могут, например, выполняться в одном процессе, и все равно есть уверенность, что эти приложения будут изолированы друг от друга. Имеется также выигрыш и в безопасности, так как возможно более точное предсказание, что данная программа будет или не будет пытаться делать.
С другой стороны, теперь нет ничего необычного в том, что программисты C++ используют какой- либо из различных классов оболочек массивов в стандартной библиотеке или в MFC, в основном для линейных массивов, чтобы получить тот же контроль границ и различные другие свойства, хотя в этом случае — без выигрыша в безопасности и производительности, связанных с возможностью анализа программы до ее выполнения.
Изменение размера массивов
Массивы C# являются динамическими, то есть можно определить число элементов в каждой размерности во время компиляции (также, как в динамически выделяемых массивах в C++). Однако невозможно изменить их размер после того, как были созданы их экземпляры. Если требуется это сделать, необходимо рассмотреть другие связанные с этим классы в пространстве имен System.Collections
в библиотеке базовых классов, таких как System.Collections.ArrayList
. Однако в этом отношении C# не отличается от C++. Обычные массивы C++ не допускают изменение размера, но существует ряд классов стандартной библиотеки, которые предоставляют это свойство.
Перечисления
В C# можно определить перечисление с помощью синтаксиса, аналогичного синтаксису C++.
// допустимо в C++ или C#
enum TypeOfBuilding {Shop, House, OfficeBlock, School};
Отметим, однако, что заключительная точка с запятой в C# не обязательна, так как определение перечисления в C# является фактически определением структуры, а определения структур не требуют заключительной точки с запятой.
// допустимо только в C#
enum TypeOfBuilding {Shop, House, OfficeBlock, School}
Однако в C# перечисление должно быть именованным, в то время как в C++ задание имени для перечисления является необязательным. Также как в C++, элементы перечисления в C# нумеруются от нуля в сторону увеличения, если только специально не определено, что элемент должен иметь определенное значение.
enum TypeOfBuilding {Shop, House=5, OfficeBlock, School = 10}
// Shop будет иметь значение 0, OfficeBlock будет иметь значение 6
Способ, с помощью которого происходит доступ к значениям элементов, отличается в C#, так как в C# необходимо определять имя перечисления.
Синтаксис C++:
TypeOfBuilding MyHouse = House;
Синтаксис C#:
TypeOfBuilding MyHouse = TypeOfBuilding.House;
Можно рассматривать это как недостаток, так как синтаксис очень велик, не это в действительности отражает тот факт, что перечисления являются в C# значительно более мощными. В C# каждое перечисление является полноценной структурой производной из System.Enum
) и поэтому имеет некоторые методы. В частности, для любого перечисленного значения можно сделать следующее:
TypeOfBuilding MyHouse = TypeOfBuilding.House;
string Result = MyHouse.ToString(); // Result будет содержать 'House'
Это почти невозможно сделать в C++.
В C# это делается и другим способом, с помощью статического метода Parse()
класса System.Enum
, хотя синтаксис будет чуть более запутанным
TypeOfВuilding MyHouse = (TypeOfBuilding)Enum.Parse(typeof(TypeOfBuilding), 'House', true);
Enum.Parse()
возвращает объектную ссылку и должен быть явно преобразован (распакован) обратно в соответствующий тип enum
. Первым параметром в Parse()
является объект System.Тyре
, который описывает, какое перечисление должна представлять строка. Второй параметр является строкой, а третий параметр указывает, должен ли игнорироваться регистр символов. Вторая перегружаемая версия опускает третий параметр и не игнорирует регистр символов.
C# позволяет также выбрать описанный ниже тип данных, используемый для хранения enum
:
enum TypeOfBuiding : short {Shop, House, OfficeBlock, School};
Если тип не указан, компилятор будет предполагать по умолчанию int
.