Шаг 60.
Язык программирования C#. Начала
Массивы. Массив объектных ссылок
На этом шаге мы рассмотрим создание и использование таких массивов.
Ранее мы всегда явно или неявно имели в виду, что элементы массива относятся к одному типу. В общем-то, это так. Но бывают и неординарные ситуации. Как мы узнаем немного позже, в языке C#
в пространстве имен System есть один особый класс, который называется Object. Его "особенность" связана с тем, что переменная типа Object может ссылаться на значение
любого типа. Проще говоря, если объявить переменную и идентификатором типа для нее указать имя класса Object, то значением такой переменной можно присвоить практически все, что угодно.
В языке C# существует механизм наследования, который позволяет создавать один класс (производный класс) на основе другого класса (базовый класс). В результате получается иерархия
классов, связанных наследованием, когда класс создается на основе класса, и на его основе создается еще один класс, и так далее. В наследовании есть важный момент: переменная базового класса
может ссылаться на объекты, созданные на основе производных классов. Специфика класса Object состоит в том, что он находится в вершине иерархии наследования и любой класс является
его прямым или косвенным потомком. Поэтому переменная типа Object может ссылаться на любой объект. Более подробно методы работы с классами и объектами, в том числе и механизм
наследования, буду рассмотрены в последующих шагах.
Если создать массив из элементов типа Object, то элементам такого массива можно присваивать значения разных типов. В некоторых случаях это бывает удобно и полезно.
На практике вместо названия класса Object обычно используют идентификатор object, который является псевдонимом для выражения System.Object.
Небольшой пример, в котором создается и используется массив с элементами типа Object, приведен ниже.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace pr60_1
{
class Program
{
static void Main()
{
// Массив из трех переменных типа Object:
Object[] objs = new Object[3];
// Элементам массива присваиваются значения
// разных типов:
objs[0] = 123; // Целое число
objs[1] = 'А'; // Символ
objs[2] = "Третий элемент"; // Текст
Console.WriteLine("Создан такой массив:");
// Проверка содержимого массива:
for(int k = 0; k < objs.Length; k++){
Console.WriteLine(k + ": " + objs[k]);
}
// Новые значения элементов:
objs[0] = (int)objs[0] + 111; // Целое число
objs[1] = "Bтopой элемент"; // Текст
objs[2] = 3.141592; // Действительное число
Console.WriteLine("После присваивания значений:");
// Проверка содержимого массива:
for(int k = 0; k < objs.Length; k++){
Console.WriteLine(k + ": " + objs[k]);
}
// Целочисленный массив:
int[] nums = {10, 20, 30};
// Переменная массива присваивается как значение
// элементу массива:
objs[2] = nums;
Console.WriteLine("Целочисленный массив:");
// Отображение элементов целочисленного массива:
for(int i = 0; i < ((int[])objs[2]).Length; i++){
Console.Write("{0,3}", ((int[])objs[2])[i]);
}
Console.WriteLine();
// Новое значение элемента в числовом массиве:
((int[])objs[2])[1] = 0;
Console.WriteLine("Еще раз тот же массив:");
// Отображение элементов целочисленного массива:
for(int i = 0; i < nums.Length; i++){
Console.Write("{0,3}", nums[i]);
}
Console.WriteLine();
// Задержка:
Console.ReadLine();
}
}
}
Архив проекта можно взять
здесь.
Результат выполнения программы будет таким, как показано ниже.

Рис.1. Результат работы приложения
Массив из трех переменных типа Object создается с помощью команды
Object[] objs = new Object[3]; .
После создания массива его элементам присваиваются значения. Делается это в явном виде, но пикантность ситуации в том, что присваиваемые значения относятся к разным типам. Так, командой
первому элементу массива
objs значением присваивается целочисленный литерал 123. Командой
второму элементу массива
objs присваивается символьное значение
'А', а командой
objs[2] = "Третий элемент";
третьему элементу массива
objs присваивается текстовое значение
"Третий элемент". С помощью оператора цикла убеждаемся, что значения элементов именно такие.
На следующем этапе элементам массива присваиваются новые значения. Причем тип присваиваемых значений может не совпадать с типом текущего значения элемента. Здесь все просто. Некоторых пояснений
требует лишь команда
objs[0] = (int)objs[0] + 111; .
Все дело в том, что на момент выполнения команды фактическим значением элемента
objs[0] является целое число. Что мы пытаемся сделать - так это к текущему значению элемента прибавить число
111. И вот здесь сталкиваемся с ситуацией, которая является платой за возможность присваивать переменным типа
Object значения разных типов: если мы хотим, чтобы с элементом
objs[0]
выполнялась арифметическая операция и сам элемент обрабатывался как целочисленное значение, то необходимо этот факт в явном виде отобразить. Именно поэтому в правой части выражения перед
ссылкой на элемент
objs[0] использована инструкция явного приведения типа
(int). Фактически, используя выражение вида
(int)objs[0], мы даем команду обрабатывать значение
элемента
objs[0] как целое число типа
int.
После изменения значений элементов массива objs мы опять проверяем содержимое массива (с помощью оператора цикла) и убеждаемся, что изменения вступили в силу.
На следующем этапе командой
int[] nums = {10, 20, 30};
создается целочисленный массив из трех элементов со значениями 10, 20 и 30. Это обычный массив. Не очень обычен способ его использования: командой
переменную массива
nums мы присваиваем значением элементу
objs[2]. Значение переменной
nums - это ссылка на массив из трех чисел. После выполнения команды
значением элемента
objs[2] является ссылка на точно тот же массив. Поэтому мы вполне можем использовать для доступа к массиву элемент
objs[2]. Но если так, то мы должны в
явном виде указать, как следует обрабатывать значение элемента
objs[2] - используется инструкция приведения типа
(int[]), означающая, что значение элемента является ссылкой на целочисленный массив.
Так мы поступаем в цикле, где индексная переменная
i принимает значения от 0 и до некоторого значения: переменная
i должна быть строго меньше значения
((int[])objs[2]).Length, которое является размером массива, на который ссылается элемент
objs[2]. Выражение
((int[])objs[2]).Length - это значение свойства
Length для
элемента
objs[2], если значение элемента интерпретировать как ссылку на одномерный целочисленный массив (значение типа
int[]). В теле конструкции цикла ссылка на элементы массива,
на который ссылается элемент
objs[2], выполняется в формате
((int[])objs[2])[i]: сначала значение элемента
objs[2] приводится к типу
int[], а затем то, что получилось, индексируется
(в квадратных скобках указывается индекс элемента).
Число 3 в инструкции {0,3} в команде
Console.Write("{0,3}", ((int[])objs[2])[i]);
означает, что при отображении значения элемента числового массива выделяется не менее трех позиций.
Убедиться в том, что переменная nums и элемент objs[2] ссылаются на один и тот же массив, достаточно легко. Командой
меняем значение второго (по порядку) элемента в массиве, на который ссылается элемент
objs[2], а затем с помощью цикла проверяем содержимое массива
nums. Несложно заметить,
что значение элемента
nums[1] действительно изменилось.
На следующем шаге мы рассмотрим параметры командной строки.
Предыдущий шаг
Содержание
Следующий шаг