Разработка приложений на C# в среде Visual Studio 2005

Контролирующий код C#

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

Массивы

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

Одномерные массивы

Так для одномерного массива синтаксис создания будет таким

тип[] имя_массива = new тип[размер];

Например, зарезервировать память под 10 элементов целого типа можно так

int[] array = new int[10];

Адресация элементов внутри массива выполняется с помощью целочисленного индекса, нумерация которого начинается с нуля. Выход индекса за пределы границ компилятором не контролируется и обнаруживается средой CLR только на этапе выполнения с генерацией исключения IndexOutOfRangeException.

Если массив объявляется как поле класса, то все элементы по умолчанию приобретают ближайшее по типу значение (для числового - нуль, для строкового - пусто, для булева - false, для ссылочного - null ). Если массив объявляется внутри метода как локальная переменная, то его перед использованием нужно явно инициализировать.

Массив в C# - это тип, производный от класса System.Array, поэтому все массивы наследуют от этого класса фиксированный набор объектных свойств и методов. Кроме того, сам класс Array содержит ряд статических методов (уровня класса), позволяющих выполнять обработку массивов как объектов.

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

int[] array = new int[10];
............
array = new int[20];

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

int[] array = { 1, 2, 3, 4, 5 };

Можно создать массив фиксированной размерности и сразу его инициализировать. Тогда размер списка инициализации должен строго соответствовать заказанной размерности массива. Например

int[] array = new int[5]{ 1, 2, 3, 4, 5 };

Допустим и такой синтаксис - без указания размерности, но с инициализацией

int[] array = new int[]{ 1, 2, 3, 4, 5 };

Можно вначале создать массив, а потом поэлементно присвоить ему значения. И тоже нужно следить, чтобы не выйти за пределы установленной размерности массива.

int[] array = new int[5];
for (int i = 0; i < array.Length; i++)    
  array[i] = i;

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

Массивы большего числа измерений имеют синтаксис, схожий с одномерными массивами.

Приведем примеры

Двумерный целочисленный массив

// Файл Program.cs
using System;
    
class MyClass
{
    public MyClass()
    {
        // Настройка консоли
        Console.Title = "Двумерный массив 5x4";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        // Работа с двумерным массивом
        const int ROWS = 5, COLUMNS = 4;// Границы
        int[,] array = new int[ROWS, COLUMNS];// Создание
        for (int count = 0; count < array.Length; count++)
            array[count / COLUMNS, count % COLUMNS] = count;// Наполнение
        for (int row = 0; row < ROWS; row++)// Перебор строк
        {
            for (int col = 0; col < COLUMNS; col++)// Перебор столбцов
            {
                string str = String.Format("a[{0},{1}]={2, -6}",
                    row, col, array[row, col]);
                Console.Write(str);
            }
            Console.WriteLine();// Новая строка
        }
    }
}
    
class Program
{
    static void Main()
    {
        new MyClass();// Чтобы сработал конструктор
        // Для задержки консольного окна
        Console.ReadLine();
    }
}

Размеры двумерного массива могут определяться компилятором автоматически по списку инициализации

// Файл Program.cs
using System;
    
class MyClass
{
    public MyClass()
    {
        // Настройка консоли
        Console.Title = "Двумерный массив со списком инициализации";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        // Размерность двумерного массива по списку инициализации
        int[,] array = {
                        {0, 1, 2, 3},
                        {4, 5, 6, 7},
                        {8, 9, 10, 11},
                        {12, 13, 14, 15},
                        {16, 17, 18, 19}
                       };
            
        const int ROWS = 5, COLUMNS = 4;// Границы для печати
    
        for (int count = 0; count < array.Length; count++)
            array[count / COLUMNS, count % COLUMNS] = count;// Наполнение
        for (int row = 0; row < ROWS; row++)// Перебор строк
        {
            for (int col = 0; col < COLUMNS; col++)// Перебор столбцов
            {
                string str = String.Format("a[{0},{1}]={2, -6}",
                    row, col, array[row, col]);
                Console.Write(str);
            }
            Console.WriteLine();// Новая строка
        }
    }
}
    
class Program
{
    static void Main()
    {
        new MyClass();// Чтобы сработал конструктор
        // Для задержки консольного окна
        Console.ReadLine();
    }
}

Трехмерные массивы

Приведем пример, из которого все ясно

// Файл Program.cs
using System;
    
class MyClass
{
    public MyClass()
    {
        // Настройка консоли
        Console.Title = "Трехмерный массив 5x4x3";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        // Работа с трехмерным массивом
        const int ROWS = 5, COLUMNS = 4, DEPTH = 3;// Границы
        int[, ,] array = new int[ROWS, COLUMNS, DEPTH];// Создание
    
        // Наполнение
        int count = 1;
        for (int i = 0; i < ROWS; i++)
            for (int j = 0; j < COLUMNS; j++)
                for (int k = 0; k < DEPTH; k++)
                    array[i, j, k] = count++;
    
        // Печать
        for (int i = 0; i < ROWS; i++)
        {
            string strTab = "";// Сбрасываем сдвиг для новой строки
            for (int k = 0; k < DEPTH; k++)// Выборка глубины
            {
                string str = "";
                for (int j = 0; j < COLUMNS; j++)
                {
                    str += String.Format("a[{0},{1},{2}]={3, -6}",
                        i, j, k, array[i, j, k]);
                }
                Console.WriteLine(strTab + str);// Строка в глубину
                strTab += "   ";// Сдвиг 3 пробела
            }
            Console.WriteLine();
        }
    }
}
    
class Program
{
    static void Main()
    {
        new MyClass();// Чтобы сработал конструктор
        // Для задержки консольного окна
        Console.ReadLine();
    }
}

Зубчатый двумерный массив

Предыдущие двумерные массивы можно сравнить с прямоугольной матрицей, все строки которой имеют одинаковое число элементов, трехмерный с кубом и т.д. Такие массивы можно назвать прямоугольными. Свойство Length определяет общее количество элементов, которое может хранить прямоугольный массив.

Но язык C# поддерживает также двумерные массивы, в которых строки могут содержать разное количество элементов. Такие массивы называются зубчатыми (или ломаный, рваный ). Зубчатый ( jagged ) двумерный массив представляет собой матрицу со строками разной длины. Зубчатые массивы используются редко, но в некоторых случаях они могут дать красивый экономный код.

Зубчатые массивы создаются в два этапа:

  • Сначала создается шаблон с фиксированным количеством строк по следующему синтаксису тип[][] имя = new тип[размер][];
  • Затем для каждой строки создается одномерный массив того же типа с заданным количеством элементов
  • В зубчатом двумерном массиве имеется два свойства Length. Если это свойство применить к самому массиву, то оно означает количество строк. Если его применить к отдельной строке, то оно будет означать длину этой строки.

    // Файл Program.cs
    using System;
        
    class MyClass
    {
        public MyClass()
        {
            // Настройка консоли
            Console.Title = "Зубчатый массив";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
        
            // Работа с зубчатым массивом
            const int ROWS = 4; // Количество строк
            int[][] jagged = new int[ROWS][]; // Создание шаблона
        
            // Создание элементов строк как одномерных массивов
            jagged[0] = new int[3];
            jagged[1] = new int[1];
            jagged[2] = new int[4];
            jagged[3] = new int[2];
        
            // Наполнение значениями
            int count = 1;
            for (int row = 0; row < jagged.Length; row++)
                for (int col = 0; col < jagged[row].Length; col++)
                    jagged[row][col] = count++;
        
            // Печать
            Console.WriteLine("Первый зубчатый массив");
            print(jagged);// Вызов вспомогательной функции
            Console.WriteLine();// Новая пустая строка
        
        
            // К прежней ссылке цепляем новый зубчатый массив
            // Старый массив остается неадресуемым
            // и становится добычей сборщика мусора GC 
            jagged = new int[][] // Шаблон и инициализация
                {
                    new int[]{1, 2, 3, 4, 5},
                    new int[]{6, 7, 8},
                    new int[]{9, 10, 11, 12},
                    new int[]{13},
                    new int[]{14, 15, 16, 17, 18, 19, 20}
                };
        
            // Печать
            Console.WriteLine("Второй зубчатый массив");
            print(jagged);// Вызов вспомогательной функции
            Console.WriteLine();// Новая пустая строка
        
            // Использование цикла foreach для перебора 
            // всех элементов массива в порядке их следования
            Console.WriteLine("Перебор с помощью foreach");
            for (int row = 0; row < jagged.Length; row++)
            {
                foreach (int elem in jagged[row])
                    Console.Write("{0, -4}", elem);
                Console.WriteLine();
            }
        }
        
        // Закатали в метод, т.к. код повторяется
        private void print(int[][] jagged)
        {
            for (int row = 0; row < jagged.Length; row++)
            {
                string str = "";
                for (int col = 0; col < jagged[row].Length; col++)
                    str += String.Format("{0, -4}", jagged[row][col]);
                Console.WriteLine(str);
            }
        }
    }
        
    class Program
    {
        static void Main()
        {
            new MyClass();// Чтобы сработал конструктор
            // Для задержки консольного окна
            Console.ReadLine();
        }
    }

    Поскольку массивы - ссылочные типы, то присвоение одного массива другому ведет к созданию ссылок-псевдонимов на одну и ту же область памяти с содержимым самого массива. Чтобы действительно получить копию массива, можно воспользоваться статическим методом Array.Copy() или объектным методом array.CopyTo().

    Индексаторы

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

    Одномерный индексатор задается по следующему синтаксису:

    // Одномерный индексатор - специфический метод класса
            тип_элемента this[int индекс]
            {
                // Аксессор чтения
                get
                {
                    // Возврат значения, 
                    // заданного элементом "индекс"
                }
                // Аксессор записи
                set
                {
                    // Установка значения для элемента,
                    // адресуемого с помощью "индекс"
                }
            }

    Тип_элемента устанавливает тип индексируемых элементов, доступ к которым предоставляет индексатор. При использовании индексатора аксессоры вызываются автоматически и могут использовать значение индекс. Если индексатор стоит слева от оператора присваивания, то срабатывает аксессор set, которому передается в переменной value значение выражения для присваивания, стоящего справа. Если индексатор стоит справа от оператора присваивания, то срабатывает аксессор get, который должен вернуть какое-то значение переменной, стоящей слева. Фактически это то же самое, что и функции доступа, только используется синтаксис массивов.

    Многомерный индексатор отличается от одномерного только тем, что вместо единственного индекса имеет список индексов, например, три индекса. Индексаторы должны быть объявлены с модификатором publi c, чтобы быть доступными во внешнем коде.

    // Многомерный индексатор
            тип_элемента this[int индекс1, int index2, int index3]
            {
                // Аксессор чтения
                get
                {
                    // Что-то делаем, используя индексы,
            // и возвращаем единственное значение
                }
                // Аксессор записи
                set
                {
                    // Что-то делаем, используя индексы
                }
            }
    // Файл Program.cs
    using System;
        
    class MyClass
    {
        public MyClass()
        {
            // Настройка консоли
            Console.Title = "Использование индексаторов";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
        
            // Создаем объект класса, инкапсулирующего целочисленный массив
            const int SIZE = 4;
            ControlBordersArray arrayControl = new ControlBordersArray(SIZE);
            // Наполняем значениями
            for (int i = 0; i < arrayControl.Length; i++)
                arrayControl[i] = i + 1;
        
            // Печатаем исходный
            Console.WriteLine("Исходный массив:");
            for (int i = 0; i < arrayControl.Length; i++)
                Console.Write("{0, -3}", arrayControl[i]);
            // Перевод на новую строку и пустая строка
            Console.WriteLine(Environment.NewLine); 
        
            // Адресуемся за границы внутреннего массива
            arrayControl[-5] = 20;
            arrayControl[5] = 30;
        
            // Опять печатаем
            Console.WriteLine("Измененный массив:");
            for (int i = 0; i < arrayControl.Length; i++)
                Console.Write("{0, -3}", arrayControl[i]);
        }
    }
        
    // Упаковка массива в класс
    class ControlBordersArray
    {
        // Закрытые поля класса
        int[] array; // Ссылка на одномерный массив
        int length;  // Длина массива
        
        // Конструктор с параметрами
        public ControlBordersArray(int size)
        {
            length = size;         // Установили параметр длины
            array = new int[length]; // Создали одномерный массив
        }
        
        // Конструктор по умолчанию
        public ControlBordersArray()
        {
            length = 16;         // Установили параметр длины
            array = new int[length]; // Создали одномерный массив
        }
        
        // Публичное свойство только для чтения
        public int Length
        {
            get { return length; }
        }
        
        // Публичный метод - индексатор
        // При выходе индекса за границы работать с пограничными элементами
        public int this[int index]
        {
            get
            {
                if (index < 0)
                    return array[0];
                else if (index >= length)
                    return array[length - 1];
                else
                    return array[index];
            }
            set
            {
                if (index < 0)
                    array[0] = value;
                else if (index >= length)
                    array[length - 1] = value;
                else
                    array[index] = value;
            }
        }
    }
        
    class Program
    {
        static void Main()
        {
            new MyClass();// Чтобы сработал конструктор
            // Для задержки консольного окна
            Console.ReadLine();
        }
    }

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

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

    // Файл Program.cs
    namespace Test
    {
        using System; // Инструкция может быть и внутри пространства имен
        
        class MyClass
        {
            public MyClass()
            {
                // Настройка консоли
                Console.Title = "Использование индексаторов";
                Console.ForegroundColor = ConsoleColor.White;
                Console.CursorVisible = false;
        
                // Создаем объект таблицы умножения
                MultiplicationTable table = new MultiplicationTable();
                string str;
        
                // Используем многомерный индексатор 
                // и распечатываем таблицу умножения
                Console.WriteLine("Таблица умножения по индексатору");
                for (int i = table.MinValue; i <= table.MaxValue; i++)
                {
                    str = "";
                    for (int j = table.MinValue; j <= table.MaxValue; j++)
                        str += String.Format("{0}x{1}={2, -3}  
                          ", i, j, table[i, j]);
                    Console.WriteLine(str);
                }
        
                // Используем перегруженный одномерный индексатор 
                // и распечатываем квадраты чисел
                Console.WriteLine(Environment.NewLine +
                    "Квадраты первых чисел натурального ряда");
                    str = "";
                    for (int i = table.MinValue; i <= table.MaxValue; i++)
                        str += String.Format("{0}x{0}={1, -3}  
                          ", i, table[i]);
                    Console.WriteLine(str);
        
                // Используем метод и распечатываем таблицу умножения
                Console.WriteLine("\r\nТаблица умножения по методу");
                for (int i = table.MinValue; i <= table.MaxValue; i++)
                {
                    str = "";
                    for (int j = table.MinValue; j <= table.MaxValue; j++)
                        str += String.Format("{0}x{1}={2, -3}  ",
                                i, j, table.Get(i, j));
                    Console.WriteLine(str);
                }
            }
        }
        
        // Имитация таблицы умножения
        class MultiplicationTable
        {
            // Свойства
            public int MinValue
            {
                get { return 1; }
            }
        
            public int MaxValue
            {
                get { return 9; }
            }
        
            // Публичный индексатор
            // имитирующий элементы таблицы умножения
            public int this[int index1, int index2]
            {
                get
                {
                int indexMin = Math.Min(index1, index2);
                int indexMax = Math.Max(index1, index2);
                if (indexMin >= MinValue  indexMax <= MaxValue)
                   return index1 * index2;
                else
                {
                Console.WriteLine("Это уже высшая математика");
                return 0;
                }
                }
            }
        
            // Обычный метод
            // Код точно такой, что и в многомерном индексаторе
            public int Get(int index1, int index2)
            {
                int indexMin = Math.Min(index1, index2);
                int indexMax = Math.Max(index1, index2);
                if (indexMin >= MinValue  indexMax <= MaxValue)
                    return index1 * index2;
                else
                {
                    Console.WriteLine("Это уже высшая математика");
                    return 0;
                }
            }
        
            // Перегруженный индексатор, возвращающий квадраты чисел
            public int this[int index]
            {
                get
                {
                if (index >= MinValue  index <= MaxValue)
                    return index * index;
                else
                {
                Console.WriteLine("Это уже высшая математика");
                return -1;
                }
                }
            }
        }
        
        class Program
        {
            static void Main()
            {
                new MyClass();// Чтобы сработал конструктор
                // Для задержки консольного окна
                Console.ReadLine();
            }
        }
    }
    Страницы:

    Массивы

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

    Одномерные массивы

    Так для одномерного массива синтаксис создания будет таким

    тип[] имя_массива = new тип[размер];

    Например, зарезервировать память под 10 элементов целого типа можно так

    int[] array = new int[10];

    Адресация элементов внутри массива выполняется с помощью целочисленного индекса, нумерация которого начинается с нуля. Выход индекса за пределы границ компилятором не контролируется и обнаруживается средой CLR только на этапе выполнения с генерацией исключения IndexOutOfRangeException.

    Если массив объявляется как поле класса, то все элементы по умолчанию приобретают ближайшее по типу значение (для числового - нуль, для строкового - пусто, для булева - false, для ссылочного - null ). Если массив объявляется внутри метода как локальная переменная, то его перед использованием нужно явно инициализировать.

    Массив в C# - это тип, производный от класса System.Array, поэтому все массивы наследуют от этого класса фиксированный набор объектных свойств и методов. Кроме того, сам класс Array содержит ряд статических методов (уровня класса), позволяющих выполнять обработку массивов как объектов.

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

    int[] array = new int[10];
    ............
    array = new int[20];

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

    int[] array = { 1, 2, 3, 4, 5 };

    Можно создать массив фиксированной размерности и сразу его инициализировать. Тогда размер списка инициализации должен строго соответствовать заказанной размерности массива. Например

    int[] array = new int[5]{ 1, 2, 3, 4, 5 };

    Допустим и такой синтаксис - без указания размерности, но с инициализацией

    int[] array = new int[]{ 1, 2, 3, 4, 5 };

    Можно вначале создать массив, а потом поэлементно присвоить ему значения. И тоже нужно следить, чтобы не выйти за пределы установленной размерности массива.

    int[] array = new int[5];
    for (int i = 0; i < array.Length; i++)    
      array[i] = i;

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

    Массивы большего числа измерений имеют синтаксис, схожий с одномерными массивами.

    Приведем примеры

    Двумерный целочисленный массив

    // Файл Program.cs
    using System;
        
    class MyClass
    {
        public MyClass()
        {
            // Настройка консоли
            Console.Title = "Двумерный массив 5x4";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
        
            // Работа с двумерным массивом
            const int ROWS = 5, COLUMNS = 4;// Границы
            int[,] array = new int[ROWS, COLUMNS];// Создание
            for (int count = 0; count < array.Length; count++)
                array[count / COLUMNS, count % COLUMNS] = count;// Наполнение
            for (int row = 0; row < ROWS; row++)// Перебор строк
            {
                for (int col = 0; col < COLUMNS; col++)// Перебор столбцов
                {
                    string str = String.Format("a[{0},{1}]={2, -6}",
                        row, col, array[row, col]);
                    Console.Write(str);
                }
                Console.WriteLine();// Новая строка
            }
        }
    }
        
    class Program
    {
        static void Main()
        {
            new MyClass();// Чтобы сработал конструктор
            // Для задержки консольного окна
            Console.ReadLine();
        }
    }

    Размеры двумерного массива могут определяться компилятором автоматически по списку инициализации

    // Файл Program.cs
    using System;
        
    class MyClass
    {
        public MyClass()
        {
            // Настройка консоли
            Console.Title = "Двумерный массив со списком инициализации";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
        
            // Размерность двумерного массива по списку инициализации
            int[,] array = {
                            {0, 1, 2, 3},
                            {4, 5, 6, 7},
                            {8, 9, 10, 11},
                            {12, 13, 14, 15},
                            {16, 17, 18, 19}
                           };
                
            const int ROWS = 5, COLUMNS = 4;// Границы для печати
        
            for (int count = 0; count < array.Length; count++)
                array[count / COLUMNS, count % COLUMNS] = count;// Наполнение
            for (int row = 0; row < ROWS; row++)// Перебор строк
            {
                for (int col = 0; col < COLUMNS; col++)// Перебор столбцов
                {
                    string str = String.Format("a[{0},{1}]={2, -6}",
                        row, col, array[row, col]);
                    Console.Write(str);
                }
                Console.WriteLine();// Новая строка
            }
        }
    }
        
    class Program
    {
        static void Main()
        {
            new MyClass();// Чтобы сработал конструктор
            // Для задержки консольного окна
            Console.ReadLine();
        }
    }

    Трехмерные массивы

    Приведем пример, из которого все ясно

    // Файл Program.cs
    using System;
        
    class MyClass
    {
        public MyClass()
        {
            // Настройка консоли
            Console.Title = "Трехмерный массив 5x4x3";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
        
            // Работа с трехмерным массивом
            const int ROWS = 5, COLUMNS = 4, DEPTH = 3;// Границы
            int[, ,] array = new int[ROWS, COLUMNS, DEPTH];// Создание
        
            // Наполнение
            int count = 1;
            for (int i = 0; i < ROWS; i++)
                for (int j = 0; j < COLUMNS; j++)
                    for (int k = 0; k < DEPTH; k++)
                        array[i, j, k] = count++;
        
            // Печать
            for (int i = 0; i < ROWS; i++)
            {
                string strTab = "";// Сбрасываем сдвиг для новой строки
                for (int k = 0; k < DEPTH; k++)// Выборка глубины
                {
                    string str = "";
                    for (int j = 0; j < COLUMNS; j++)
                    {
                        str += String.Format("a[{0},{1},{2}]={3, -6}",
                            i, j, k, array[i, j, k]);
                    }
                    Console.WriteLine(strTab + str);// Строка в глубину
                    strTab += "   ";// Сдвиг 3 пробела
                }
                Console.WriteLine();
            }
        }
    }
        
    class Program
    {
        static void Main()
        {
            new MyClass();// Чтобы сработал конструктор
            // Для задержки консольного окна
            Console.ReadLine();
        }
    }

    Зубчатый двумерный массив

    Предыдущие двумерные массивы можно сравнить с прямоугольной матрицей, все строки которой имеют одинаковое число элементов, трехмерный с кубом и т.д. Такие массивы можно назвать прямоугольными. Свойство Length определяет общее количество элементов, которое может хранить прямоугольный массив.

    Но язык C# поддерживает также двумерные массивы, в которых строки могут содержать разное количество элементов. Такие массивы называются зубчатыми (или ломаный, рваный ). Зубчатый ( jagged ) двумерный массив представляет собой матрицу со строками разной длины. Зубчатые массивы используются редко, но в некоторых случаях они могут дать красивый экономный код.

    Зубчатые массивы создаются в два этапа:

  • Сначала создается шаблон с фиксированным количеством строк по следующему синтаксису тип[][] имя = new тип[размер][];
  • Затем для каждой строки создается одномерный массив того же типа с заданным количеством элементов
  • В зубчатом двумерном массиве имеется два свойства Length. Если это свойство применить к самому массиву, то оно означает количество строк. Если его применить к отдельной строке, то оно будет означать длину этой строки.

    // Файл Program.cs
    using System;
        
    class MyClass
    {
        public MyClass()
        {
            // Настройка консоли
            Console.Title = "Зубчатый массив";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
        
            // Работа с зубчатым массивом
            const int ROWS = 4; // Количество строк
            int[][] jagged = new int[ROWS][]; // Создание шаблона
        
            // Создание элементов строк как одномерных массивов
            jagged[0] = new int[3];
            jagged[1] = new int[1];
            jagged[2] = new int[4];
            jagged[3] = new int[2];
        
            // Наполнение значениями
            int count = 1;
            for (int row = 0; row < jagged.Length; row++)
                for (int col = 0; col < jagged[row].Length; col++)
                    jagged[row][col] = count++;
        
            // Печать
            Console.WriteLine("Первый зубчатый массив");
            print(jagged);// Вызов вспомогательной функции
            Console.WriteLine();// Новая пустая строка
        
        
            // К прежней ссылке цепляем новый зубчатый массив
            // Старый массив остается неадресуемым
            // и становится добычей сборщика мусора GC 
            jagged = new int[][] // Шаблон и инициализация
                {
                    new int[]{1, 2, 3, 4, 5},
                    new int[]{6, 7, 8},
                    new int[]{9, 10, 11, 12},
                    new int[]{13},
                    new int[]{14, 15, 16, 17, 18, 19, 20}
                };
        
            // Печать
            Console.WriteLine("Второй зубчатый массив");
            print(jagged);// Вызов вспомогательной функции
            Console.WriteLine();// Новая пустая строка
        
            // Использование цикла foreach для перебора 
            // всех элементов массива в порядке их следования
            Console.WriteLine("Перебор с помощью foreach");
            for (int row = 0; row < jagged.Length; row++)
            {
                foreach (int elem in jagged[row])
                    Console.Write("{0, -4}", elem);
                Console.WriteLine();
            }
        }
        
        // Закатали в метод, т.к. код повторяется
        private void print(int[][] jagged)
        {
            for (int row = 0; row < jagged.Length; row++)
            {
                string str = "";
                for (int col = 0; col < jagged[row].Length; col++)
                    str += String.Format("{0, -4}", jagged[row][col]);
                Console.WriteLine(str);
            }
        }
    }
        
    class Program
    {
        static void Main()
        {
            new MyClass();// Чтобы сработал конструктор
            // Для задержки консольного окна
            Console.ReadLine();
        }
    }

    Поскольку массивы - ссылочные типы, то присвоение одного массива другому ведет к созданию ссылок-псевдонимов на одну и ту же область памяти с содержимым самого массива. Чтобы действительно получить копию массива, можно воспользоваться статическим методом Array.Copy() или объектным методом array.CopyTo().

    Индексаторы

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

    Одномерный индексатор задается по следующему синтаксису:

    // Одномерный индексатор - специфический метод класса
            тип_элемента this[int индекс]
            {
                // Аксессор чтения
                get
                {
                    // Возврат значения, 
                    // заданного элементом "индекс"
                }
                // Аксессор записи
                set
                {
                    // Установка значения для элемента,
                    // адресуемого с помощью "индекс"
                }
            }

    Тип_элемента устанавливает тип индексируемых элементов, доступ к которым предоставляет индексатор. При использовании индексатора аксессоры вызываются автоматически и могут использовать значение индекс. Если индексатор стоит слева от оператора присваивания, то срабатывает аксессор set, которому передается в переменной value значение выражения для присваивания, стоящего справа. Если индексатор стоит справа от оператора присваивания, то срабатывает аксессор get, который должен вернуть какое-то значение переменной, стоящей слева. Фактически это то же самое, что и функции доступа, только используется синтаксис массивов.

    Многомерный индексатор отличается от одномерного только тем, что вместо единственного индекса имеет список индексов, например, три индекса. Индексаторы должны быть объявлены с модификатором publi c, чтобы быть доступными во внешнем коде.

    // Многомерный индексатор
            тип_элемента this[int индекс1, int index2, int index3]
            {
                // Аксессор чтения
                get
                {
                    // Что-то делаем, используя индексы,
            // и возвращаем единственное значение
                }
                // Аксессор записи
                set
                {
                    // Что-то делаем, используя индексы
                }
            }
    // Файл Program.cs
    using System;
        
    class MyClass
    {
        public MyClass()
        {
            // Настройка консоли
            Console.Title = "Использование индексаторов";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
        
            // Создаем объект класса, инкапсулирующего целочисленный массив
            const int SIZE = 4;
            ControlBordersArray arrayControl = new ControlBordersArray(SIZE);
            // Наполняем значениями
            for (int i = 0; i < arrayControl.Length; i++)
                arrayControl[i] = i + 1;
        
            // Печатаем исходный
            Console.WriteLine("Исходный массив:");
            for (int i = 0; i < arrayControl.Length; i++)
                Console.Write("{0, -3}", arrayControl[i]);
            // Перевод на новую строку и пустая строка
            Console.WriteLine(Environment.NewLine); 
        
            // Адресуемся за границы внутреннего массива
            arrayControl[-5] = 20;
            arrayControl[5] = 30;
        
            // Опять печатаем
            Console.WriteLine("Измененный массив:");
            for (int i = 0; i < arrayControl.Length; i++)
                Console.Write("{0, -3}", arrayControl[i]);
        }
    }
        
    // Упаковка массива в класс
    class ControlBordersArray
    {
        // Закрытые поля класса
        int[] array; // Ссылка на одномерный массив
        int length;  // Длина массива
        
        // Конструктор с параметрами
        public ControlBordersArray(int size)
        {
            length = size;         // Установили параметр длины
            array = new int[length]; // Создали одномерный массив
        }
        
        // Конструктор по умолчанию
        public ControlBordersArray()
        {
            length = 16;         // Установили параметр длины
            array = new int[length]; // Создали одномерный массив
        }
        
        // Публичное свойство только для чтения
        public int Length
        {
            get { return length; }
        }
        
        // Публичный метод - индексатор
        // При выходе индекса за границы работать с пограничными элементами
        public int this[int index]
        {
            get
            {
                if (index < 0)
                    return array[0];
                else if (index >= length)
                    return array[length - 1];
                else
                    return array[index];
            }
            set
            {
                if (index < 0)
                    array[0] = value;
                else if (index >= length)
                    array[length - 1] = value;
                else
                    array[index] = value;
            }
        }
    }
        
    class Program
    {
        static void Main()
        {
            new MyClass();// Чтобы сработал конструктор
            // Для задержки консольного окна
            Console.ReadLine();
        }
    }

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

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

    // Файл Program.cs
    namespace Test
    {
        using System; // Инструкция может быть и внутри пространства имен
        
        class MyClass
        {
            public MyClass()
            {
                // Настройка консоли
                Console.Title = "Использование индексаторов";
                Console.ForegroundColor = ConsoleColor.White;
                Console.CursorVisible = false;
        
                // Создаем объект таблицы умножения
                MultiplicationTable table = new MultiplicationTable();
                string str;
        
                // Используем многомерный индексатор 
                // и распечатываем таблицу умножения
                Console.WriteLine("Таблица умножения по индексатору");
                for (int i = table.MinValue; i <= table.MaxValue; i++)
                {
                    str = "";
                    for (int j = table.MinValue; j <= table.MaxValue; j++)
                        str += String.Format("{0}x{1}={2, -3}  
                          ", i, j, table[i, j]);
                    Console.WriteLine(str);
                }
        
                // Используем перегруженный одномерный индексатор 
                // и распечатываем квадраты чисел
                Console.WriteLine(Environment.NewLine +
                    "Квадраты первых чисел натурального ряда");
                    str = "";
                    for (int i = table.MinValue; i <= table.MaxValue; i++)
                        str += String.Format("{0}x{0}={1, -3}  
                          ", i, table[i]);
                    Console.WriteLine(str);
        
                // Используем метод и распечатываем таблицу умножения
                Console.WriteLine("\r\nТаблица умножения по методу");
                for (int i = table.MinValue; i <= table.MaxValue; i++)
                {
                    str = "";
                    for (int j = table.MinValue; j <= table.MaxValue; j++)
                        str += String.Format("{0}x{1}={2, -3}  ",
                                i, j, table.Get(i, j));
                    Console.WriteLine(str);
                }
            }
        }
        
        // Имитация таблицы умножения
        class MultiplicationTable
        {
            // Свойства
            public int MinValue
            {
                get { return 1; }
            }
        
            public int MaxValue
            {
                get { return 9; }
            }
        
            // Публичный индексатор
            // имитирующий элементы таблицы умножения
            public int this[int index1, int index2]
            {
                get
                {
                int indexMin = Math.Min(index1, index2);
                int indexMax = Math.Max(index1, index2);
                if (indexMin >= MinValue  indexMax <= MaxValue)
                   return index1 * index2;
                else
                {
                Console.WriteLine("Это уже высшая математика");
                return 0;
                }
                }
            }
        
            // Обычный метод
            // Код точно такой, что и в многомерном индексаторе
            public int Get(int index1, int index2)
            {
                int indexMin = Math.Min(index1, index2);
                int indexMax = Math.Max(index1, index2);
                if (indexMin >= MinValue  indexMax <= MaxValue)
                    return index1 * index2;
                else
                {
                    Console.WriteLine("Это уже высшая математика");
                    return 0;
                }
            }
        
            // Перегруженный индексатор, возвращающий квадраты чисел
            public int this[int index]
            {
                get
                {
                if (index >= MinValue  index <= MaxValue)
                    return index * index;
                else
                {
                Console.WriteLine("Это уже высшая математика");
                return -1;
                }
                }
            }
        }
        
        class Program
        {
            static void Main()
            {
                new MyClass();// Чтобы сработал конструктор
                // Для задержки консольного окна
                Console.ReadLine();
            }
        }
    }
    Вернуться к учебному плану