Скачиваний:
64
Добавлен:
24.03.2015
Размер:
267.26 Кб
Скачать

15.4. Упаковка и распаковка

Когда значение структурного типа преобразуется к типу object или приводится к типу того интерфейса, который реализован структурой, выполняется операция упаковки (boxing). Эта операция выполняется автоматически и не требует вмешательства программиста.

Упаковкой называют процесс явного преобразования из типа значений в тип ссылок. При упаковке создаётся и размещается куче объект, которому присваивается значение объекта с типом значения. Возвращаемым значением при выполнении упаковки служит ссылка на объект кучи.

Обратной операцией является распаковка (unboxing), при которой значение объекта присваивается переменной с типом значения.

Автоматическая упаковка выполняется в тех случаях, когда выполняется допустимое присваивание ссылке на объект ссылочного типа переменной с типом значения. Так как все классы языка С# имеют общий базовый класс object , то ссылке типа object можно присвоить значение структуры. В этом случае выполняется автоматическая упаковка, не требующая вмешательства программиста. Обратная процедура - распаковка - автоматически не выполняется. Для распаковки необходимо применять операцию приведения типов.

Сказанное относится не только к присваиванию, но и к передаче параметров и к возвращаемому методом результату. Рассмотрим статический метод с таким заголовком:

static object redouble(object obj)

Метод принимает ссылку на объект типа object и возвращает значение того же типа. Внешне ничто не препятствует применению в обращении к этому методу в качестве аргумента ссылки на объект любого типа. Однако, в теле метода необходимо учитывать конкретный тип аргумента, и формировать возвращаемый результат в соответствии с этим типом. В следующей программе определена структура Struct1 с полем x типа double, и метод с приведенным выше заголовком.

// 15_10.cs - структуры, упаковка, распаковка

struct Struct1 // структура

{

double x;

public double X { get { return x; } set { x = value; } }

}

static object reDouble(object obj)

{

if (obj is Struct1)

{

Struct1 st = (Struct1)obj;

st.X = 2 * st.X;

return st;

}

else

Console.WriteLine("Неизвестный тип!");

return obj;

}

public static void Main()

{

Struct1 one = new Struct1();

one.X = 4;

Struct1 two = (Struct1)reDouble(one);

Console.WriteLine("one.X={0}; two.X={1}", one.X, two.X);

Console.WriteLine("(int)reDouble(55)={0}", (int)reDouble(55));

}

Результат выполнения программы: one.X=4; two.X=8 Неизвестный тип! (int)reDouble(55) = 55

Метод reDouble() обрабатывает только аргументы типа Struct1, хотя ему можно передать аргумент любого типа. Если аргумент имеет тип Struct1, метод reDouble() выполняет его распаковку и удваивает значение поля double х. Если тип аргумента отличен от Struct1, то тип распознаётся как неизвестный и аргумент возвращается в точку вызова в «упакованном» виде. Для примера в методе Main() обращение к reDouble() выполнено дважды с аргументами разных типов.

Понимание процедур упаковки и распаковки необходимо для применения таких библиотечных средств как коллекции. К ним относится класс ArrayList (массив-список) из пространства имён System.Collection. Объект класса ArrayList во многих случаях «ведёт себя» как массив. Например, к нему применима индексация. В отличие от массивов, производных от класса Array, объекты класса ArrayList могут расти в процессе выполнения программы. Количество их элементов увеличивается, как только в этом возникает необходимость, причём рост выполняется автоматически без вмешательства программиста.

Объявление такого растущего массива-списка:

using System.Collections;

ArrayList dinamo = new ArrayList(3);

В данном примере объявлена ссылка dinamo и ассоциированный с нею объект класса ArrayList. В обращении к конструктору ArrayList() указан начальный размер массива-списка. Можно предположить, что теперь можно обращаться к элементам массива-списка, используя индексы со значениями 0, 1, 2. Однако следующая попытка будет ошибочной:

dinamo [1]=45.3; // ошибка времени исполнения

Даже, если в вызове конструктора указан размер массива-списка, первоначально элементов в создаваемом массиве-списке НЕТ! Отличие объекта класса ArrayList от традиционного массива состоит в том, что в массив-список элементы должны быть вначале занесены с помощью нестатического метода Add() класса ArrayList.

Заголовок метода:

public virtual int Add(object value)

Метод добавляет в конец массива-списка элемент, заданный аргументом. Возвращаемое значение - порядковый номер (индекс) добавленного элемента. Нумерация элементов начинается с нуля.

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

В следующей программе создан массив-список, представляемый ссылкой ArrayList dynamo. Затем в этот массив-список добавлены элементы со значениями double, int и Points, где Points - пользовательский тип, объявленный в программе как структура. Текст программы:

// 15_11.cs - структуры и массив-список типа ArrayList

struct PointS // структура

{

double x, y;

public double X { get { return x; } set { x = value; } }

public double Y { get { return y; } set { y = value; } }

}

public static void Main11()

{

ArrayList dinamo = new ArrayList();

dinamo.Add(4.8);

dinamo.Add(new PointS());

dinamo.Add(100);

PointS ps = new PointS();

ps.X = 10.2;

dinamo.Add(ps);

dinamo[1] = 1.23;

foreach (object ob in dinamo)

if (ob is PointS)

Console.WriteLine("Struct: X={0}; Y={1}", ((PointS)ob).X, ((PointS)ob).Y);

else

if (ob is Double)

Console.WriteLine("Double: Value={0}", ((double)ob).ToString());

}

Результат выполнения программы: Double: Value=4,8 Double: Value=1,23 Struct: X=10,2; Y=0

В методе Main() после размещения в массиве-списке dinamo четырёх элементов, эти элементы перебираются в цикле foreach. Параметр цикла ob имеет тип object. Ему последовательно присваиваются ссылки на разнотипные элементы массива-списка. Непосредственно использовать параметр типа object для доступа к объектам разных типов невозможно - у каждого типа своя структура, свои члены, свои поля. В теле цикла условные операторы, предназначенные для распознавания типов. Распознаются только типы double и Points. Обратите внимание как с помощью выражения с индексацией выполнено присваивание второму элементу массива-списка:

dinamo[1] = 1.23;

Перед этим присваиванием значением элемента dinamo[1] был объект структуры Points.

Соседние файлы в папке Lekc_C#