- •Предисловие
- •Объектная ориентация программ на C#
- •1.1. Типы, классы, объекты
- •1.2. Программа на C#
- •1.3. Пространство имен
- •1.4. Создание консольного приложения
- •Типы в языке C#
- •2.1. Типы ссылок и типы значений
- •2.2. Классификация типов C#
- •2.3. Простые типы. Константы-литералы
- •2.4. Объявления переменных и констант базовых типов
- •Операции и целочисленные выражения
- •3.1. Операции языка C#
- •3.2. Операции присваивания и оператор присваивания
- •3.3. Операции инкремента (++) и декремента (--)
- •3.4. Выражения с арифметическими операциями
- •3.5. Поразрядные операции
- •Выражения с операндами базовых типов
- •4.1. Автоматическое и явное приведение арифметических типов
- •4.2. Особые ситуации в арифметических выражениях
- •4.3. Логический тип и логические выражения
- •4.4. Выражения с символьными операндами
- •5.2. Простые (базовые) типы C# как классы
- •Операторы
- •6.1. Общие сведения об операторах
- •6.2. Метки и оператор безусловного перехода
- •6.4. Операторы цикла
- •6.5. Операторы передачи управления
- •6.6. Переключатель
- •Массивы
- •7.1. Одномерные массивы
- •7.2. Массивы как наследники класса Array
- •7.3. Виды массивов и массивы многомерные
- •7.5. Массивы массивов и поверхностное копирование
- •Строки – объекты класса string
- •8.1. Строковые литералы
- •8.2. Строковые объекты и ссылки типа string
- •8.3. Операции над строками
- •8.5. Форматирование строк
- •8.6. Строка как контейнер
- •8.7. Применение строк в переключателях
- •8.8. Массивы строк
- •8.8. Сравнение строк
- •8.10. Аргументы метода Main()
- •Методы C#
- •9.2. Соотношение фиксированных параметров и аргументов
- •9.3. Параметры с типами ссылок
- •9.4. Методы с переменным числом аргументов
- •9.5. Перегрузка методов
- •9.6. Рекурсивные методы
- •9.7. Применение метода Array.Sort()
- •Класс как совокупность статических членов
- •10.1. Статические члены класса
- •10.2. Поля классов (статические поля)
- •10.3. Статические константы
- •10.4. Статические методы
- •10.5. Статический конструктор
- •10.6. Статические классы
- •Классы как типы
- •11.1. Объявление класса
- •11.2. Поля объектов
- •11.3. Объявления методов объектов
- •11.4. Пример класса и его объектов
- •11.5. Ссылка this
- •11.6. Конструкторы объектов класса
- •11.7. Деструкторы и финализаторы
- •Средства взаимодействия с объектами
- •12.1. Принцип инкапсуляции и методы объектов
- •12.2. Свойства классов
- •12.3. Автореализуемые свойства
- •12.4. Индексаторы
- •12.5. Индексаторы, имитирующие наличие контейнера
- •Включение, вложение и наследование классов
- •13.1. Включение объектов классов
- •13.2. Вложение классов
- •13.3. Наследование классов
- •13.4. Доступность членов класса при наследовании
- •13.5. Методы при наследовании
- •13.6. Абстрактные методы и абстрактные классы
- •13.7. Опечатанные классы и методы
- •13.8. Применение абстрактых классов
- •Интерфейсы
- •14.1. Два вида наследования в ООП
- •14.2. Объявления интерфейсов
- •14.3. Реализация интерфейсов
- •14.4. Интерфейс как тип
- •14.5. Интерфейсы и наследование
- •Перечисления и структуры
- •15.1. Перечисления
- •15.2. Базовый класс перечислений
- •15.3. Структуры
- •15.4. Упаковка и распаковка
- •15.5. Реализация структурами интерфейсов
- •Исключения
- •16.1. О механизме исключений
- •16.3. Свойства исключений
- •16.5. Исключения в арифметических выражениях
- •16.6. Генерация исключений
- •16.7. Пользовательские классы исключений
- •Делегаты и события
- •17.1. Синтаксис делегатов
- •17.2. Массивы делегатов
- •17.3. Многоадресные групповые экземпляры делегатов
- •17.4. Делегаты и обратные вызовы
- •17.5. Анонимные методы
- •17.6. События
- •Литература
- •Предметный указатель
114 |
Г л а в а 7 |
|
|
static void Main()
{
double[,] ar = {
{10, -7, 0, 7},
{-3, 2.099, 6, 3.901},
{5, -1, 5, 6},
};
Console.WriteLine("ar.Rank = " + ar.Rank); Console.WriteLine("ar. = " + ar.GetUpperBound(1); Console.WriteLine("ar.GetLength(1) = " +
ar.GetLength(1));
for (int i = 0; i < ar.GetLength(0); i++, Console. WriteLine())
for (int j = 0; j <= ar.GetUpperBound(1); j++) Console.Write("\t"+ ar[i, j]);
}
}
Результат выполнения программы:
ar.Rank = 2 |
|
|
|
ar.GetUpperBound(1) = 3 |
|
|
|
ar.GetLength(1) = 4 |
|
|
|
10 -7 |
0 |
7 |
|
3 |
2,099 |
6 |
3,901 |
5 |
-1 |
5 |
6 |
В программе определён и инициализирован двумерный массив с элементами типа double. Результаты выполнения программы поясняют особенности свойств и методов типа массивов, производного от класса Array. Обратите внимание, что GetUpperBound(1) – верхняя граница второго индекса, а не количество значений этого индекса.
7.5. Массивы массивов и поверхностное копирование
Итак, массив массивов представляет собой одномерный массив, элементами которого являются ссылки на массивы следующего (подчиненного) уровня. Этот факт требует особого внимания, так как затрагивает фундаментальные вопросы ко-
Массивы |
115 |
|
|
пирования ссылок и тех объектов, которые ими адресуются. Независимо от того, какого вида массив мы рассматриваем,
присваивание ссылке на массив значения другой ссылки на уже существующий массив (на объект с типом массива) приводит к появлению двух ссылок на один массив. Это мы уже иллюстрировали и разобрали.
Метод Clone() позволяет создать новый экземпляр массива. В программе 07_04.cs показано, что, изменяя один из одномерных массивов-копий, мы не изменяем второй. Следующая программа иллюстрирует применение копирования к многомерному массиву:
// 07_08.cs – двумерный массив – полное клонирование using System;
class Program
{
static void Main()
{
int size;
do Console.Write("size = ");
while (!int.TryParse(Console.ReadLine(), out size)||size<1);
int[,] one = new int[size, size]; Console.WriteLine("Массив one:");
for (int i = 0; i < size; i++, Console.WriteLine()) for (int j = 0; j < size; j++)
{
if (i == j) one[i, j] = 1; Console.Write(one[i, j] + "\t");
}
Console.WriteLine("one.Length = " + one.Length); int[,] two = (int[,])one.Clone(); // клонирование two[0, 0] = -size;
Console.WriteLine("Массив two:");
for (int i = 0; i < size; i++, Console.WriteLine()) for (int j = 0; j < size; j++)
Console.Write(two[i, j] + "\t"); Console.WriteLine("Массив one:");
for (int i = 0; i < size; i++, Console.WriteLine()) for (int j = 0; j < size; j++)
Console.Write(one[i, j] + "\t");
} }
116 Г л а в а 7
Результат выполнения программы:
size = 4<ENTER> |
|
||
Массив one: |
|
||
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
one.Length = 16 |
|
||
Массив two: |
|
||
-4 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
Массив one: |
|
||
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
0 |
0 |
0 |
0 |
1 |
В программе определена ссылка two типа int[,], и ей присвоен результат копирования массива, связанного со ссылкой one, имеющей тот же тип int[,]. Выведена единичная матрица, адресованная ссылкой one, затем изменен обычным присваиванием один элемент массива–копии:
two[0,0]=-size;
Выведенные на консоль значения элементов массивов иллюстрируют независимость массива-оригинала от массива-копии.
Программы 07_04.cs и 07_08.cs работают с массивами, у которых по одному спецификатору размерности. В первом случае массив одномерный, во второй программе клонируется двумерный массив. Применяя метод Clone() к массиву массивов, мы сталкиваемся с очень важной особенностью. Строго говоря, действия метода остаются прежними – он создает массив-ко- пию и присваивает его элементам значения элементов масси- ва-оригинала. Однако в этом случае копирования тех подчинённых массивов, на которые "смотрят" ссылки-элементы мас- сива-оригинала, не происходит. Выполняется так называемое поверхностное или поразрядное копирование. Иначе и быть не должно – "не знает" метод Clone(), что код, который является
Массивы |
117 |
|
|
значением элемента массива, представляет собой ссылку, и по этой ссылке нужно еще что-то "доставать".
Таким образом, копируя с помощью метода Clone() массив массивов, мы получаем два экземпляра массива верхнего уровня, элементы которых адресуют одни и те же участки памяти, выделенные для подчиненных массивов объекта-оригинала.
В качестве иллюстрации указанной ситуации приведем следующую программу, построенную на основе 07_06.cs:
/ 07_09.cs – непрямоугольный массив – клонирование
поверхностное! using System; class Program
{
static void Main()
{
int size;
do Console.Write("size = ");
while (!int.TryParse(Console.ReadLine(), out size)||size<1);
int[][] tre = new int[size][]; for (int j = 0; j < size; j++)
{
tre[j] = new int[j + 1]; tre[j][j] = j + 1;
}
Console.WriteLine("Массив tre:");
for (int i = 0; i < tre.Length; i++, Console.WriteLine()) for (int j = 0; j < tre[i].Length; j++)
Console.Write(tre[i][j] + "\t"); Console.WriteLine("tre.Length = " + tre.Length); int[][] two = (int[][])tre.Clone();
two[0][0] = - size; Console.WriteLine("Массив two:");
for (int i = 0; i < two.Length; i++, Console.WriteLine()) for (int j = 0; j < two[i].Length; j++)
Console.Write(two[i][j] + "\t"); Console.WriteLine("Массив tre:");
for (int i = 0; i < tre.Length; i++, Console.WriteLine()) for (int j = 0; j < tre[i].Length; j++)
Console.Write(tre[i][j] + "\t");
} }
118 |
Г л а в а 7 |
|
|
Результат выполнения программы:
size = 4<ENTER>
Массив tre:
1
02
0 |
0 |
3 |
|
0 |
0 |
0 |
4 |
tre.Length = 4
Массив two: -4
02
0 |
0 |
3 |
|
0 |
0 |
0 |
4 |
Массив tre: |
|
||
-4 |
|
|
|
02
0 0 3
0 0 0 4
В программе определена ссылка two типа int[][], и ей присвоен результат копирования (клонирования) "треугольного" массива, адресованного ссылкой tre, имеющей тип int[][]. С помощью оператора
two[0][0] = - size;
изменен один целочисленный элемент «нижнего уровня» массива массивов. После присваивания изменилось значение, соответствующее выражению tre[0][0].
Контрольные вопросы
1.Являются ли типы массивов типами значений?
2.Какое значение имеет индексирующее выражение при обращении к первому элементу одномерного массива?
3.Какой тип может иметь индексирующее выражение?
4.Где размещается (в стеке или в управляемой куче) ссылка на массив?
5.При выполнении какой операции создается объект класса массивов?
6.Что такое класс массивов?
Массивы |
119 |
|
|
7.Какие значения принимают элементы массива при отсутствии в его определении инициализатора?
8.Какова структура инициализатора массива?
9.Чем определяется количество инициализирующих выражений в инициализаторе массива?
10.Объясните назначение всех элементов цикла foreach.
11.Каково назначение и возможности переменной цикла foreach.
12.Можно ли изменить размер массива-объекта после его создания?
13.Можно ли определить размер массива-объекта в процессе выполнения программы?
14.Назовите свойства массивов, унаследованные ими от класса Array.
15.Приведите примеры нестатических методов одномерных массивов.
16.Приведите примеры статических методов одномерных массивов.
17.В чём различия методов Copy() и Clone()?
18.Что такое размерность массива?
19.Что такое спецификатор размерности массива?
20.Допустимо ли динамическое определение размеров многомерных массивов?
21.Чему равно свойство Length для многомерного массива?
22.С помощью каких средств можно получить размер многомерного массива по нужному измерению?
23.Сколько спецификаторов размерности в объявлении типа четырёхмерного массива?
24.Перечислите синтаксические отличия массива массивов от двумерного массива.
25.Сколько операций new в определении объекта трехмерного массива?
26.Чему равно свойство Rank массива массивов?
27.В каком случае при клонировании массива проявляется эффект поверхностного копирования?