Разработка приложений на языке C#. Полезные настройки. Особые спецификаторы формата, страница 15

Теперь, ссылка men указывает на другой массив. Но, куда делись предыдущие элементы массива? Первый массив из трех ссылок, а также три объекта класса Man, на которые он ссылался, стали мусором, который уберет garbage collector. Когда он это сделает? Для ответа достаточно поставить точку останова (F9) на единственной строке кода в теле деструктора класса Man и запустить приложение в режиме отладки (F5).

Выполнение дойдет до точки останова только в момент выхода из программы. Посмотрите в этот момент в окно Autos (в блоке окон внизу экрана). В нем вы должны видеть имена погибающих объектов. Каждое последующее нажатие F5 демонстрирует следующий объект, для которого был вызван деструктор. Но кто вызывает деструкторы? На этот вопрос даст ответ окно отладчика Call Stack, в котором обычно вы наблюдаете цепочку вызовов функций, хранящуюся в стеке.

Активизируйте его способом, аналогичным тому, что вы применняли для окна Autos, и убедитесь, что в нем присутствует строка, которая сообщает о том, что деструктор был вызван из функции Finalize, унаследованной нашим классом от Object. Эта виртуальная функция вызывается в процессе сборки мусора, но точный момент времени, когда это произойдет предсказать невозможно и не нужно. Это прерогатива CLR.

Отметьте, что в C# и в управляемой (managed) версии C++ мы не должны переопределять (override) Finalize в пользовательском классе (например, в классе Man). Если мы хотим произвести какие-то действия до того, как сборщик мусора уничтожит наш объект, то мы должны сделать это в теле деструктора.

Так как все массивы — это ссылки (reference types), то нельзя при объявлении массива использовать синтаксис языка С++, например: Man mm[2];. Вместо этого надо использовать синтаксис: Man[] mm = new Man[2];. Однако, вы видели объявление вида: Man[] men = { . . . }. Здесь операция new используется неявно. Такой стиль является сокращением в угоду привычке программистов на С++, коими в данное время являются почти все, кто изучает C#.

Многомерные массивы

Массивы могут иметь более одного измерения. Многомерный массив переменных любого типа можно объявить двумя способами. Первый способ несколько непривычен для тех, кто привык работать с конструкциями языка С++. Второй — более понятен, но тоже требует усилий и времени на привыкание.

Важно отличать многомерные массивы (multidimensional arrays) и массивы массивов (jagged arrays). Элементы многомерных массивов расположены построчно (подряд в памяти), а элементы (например, двухмерных) jagged arrays представляют собой ссылки на одномерные массивы. Эти ссылки также расположены подряд в памяти, но сами массивы могут быть расположены в разных местах памяти (не подряд).

Если вам надоело дополнять функцию Main новыми ветвями оператора switch, то вы можете все манипуляции с массивами выполнить в одной ветви, которая может иметь такой вид.

case 'm': TestMan(); TestArray(); break;

Создайте (методом Ctrl+Drag) файл TestArray для нового примера, и поместите внутрь скопированной заготовки такой код.

uint[,] ua =   // Объявляем многомерный массив целых

{

  {1,2,3},

  {4,5,6}

};

Console.WriteLine ("2-dimensional array:\n");

for (int i=0; i <= ua.GetUpperBound(0); i++, Console.WriteLine())// Пробег по нулевому измерению

{

  for (int j=0; j < ua.GetLength(1); j++)// Пробег по первому измерению

      Console.Write ("a[{0},{1}] = {2},  ", i, j, ua[i,j]);

}

Console.WriteLine("\n\nRank = " + ua.Rank);       // Хотим увидеть значения фундаментальных свойств

Console.WriteLine("Rows = " + ua.GetLength(0));

Console.WriteLine("Columns = " + ua.GetLength(1));

Console.WriteLine("Length = " + ua.Length);

Значительно меньше усилий требуется для бесформатного вывода массива. Для этого вместо двух вложенных циклов for используйте цикл foreach.

Console.WriteLine("\n\nUnformatted output\n");

foreach (uint i in ua)  // Пробег по всем элементам массива

  Console.Write (i+", ");