Структуры

Структура — тип данных, аналогичный классу, но имеющий ряд важных отли­чий от него:

□ структура является значимым, а не ссылочным типом данных, то есть экземпляр структуры хранит значения своих элементов, а не ссылки на них, и располагается в стеке, а не в хипе;

□ структура не может участвовать в иерархиях наследования, она может только
реализовывать интерфейсы;

□ в структуре запрещено определять конструктор по умолчанию, поскольку он
определен неявно и присваивает всем ее элементам значения по умолчанию
(нули соответствующего типа);

□ в структуре запрещено определять деструкторы, поскольку это бессмысленно.

ПРИМЕЧАНИЕ

Строго говоря, любой значимый тип С# является структурным.

Отличия от классов обусловливают область применения структур: типы данных, имеющие небольшое количество полей, с которыми удобнее работать как со зна­чениями, а не как со ссылками. Накладные расходы на динамическое выделение памяти для небольших объектов могут весьма значительно снизить быстродействие программы, поэтому их эффективнее описывать как структуры, а не как классы.

ПРИМЕЧАНИЕ

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

Синтаксис структуры:

[ атрибуты ] [ спецификаторы ] struct имя_структуры [: интерфейсы ]

тело_структуры [; ]

Спецификаторы структуры имеют такой же смысл, как и для класса, причем из спецификаторов доступа допускаются только public, internal и private (послед­ний — только для вложенных структур).

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

могут использоваться спецификаторы protected и protected internal; □ структуры не могут быть абстрактными (abstract), к тому же по умолчанию

они бесплодны (sealed);

□ методы структур не могут быть абстрактными и виртуальными;

□ переопределяться (то есть описываться со спецификатором override) могут
только методы, унаследованные от базового класса object;

□ параметр this интерпретируется как значение, поэтому его можно использовать для ссылок, но не для присваивания;

□ при описании структуры нельзя задавать значения полей по умолчанию1 -
это будет сделано в конструкторе по умолчанию, создаваемом автоматически
(конструктор присваивает значимым полям структуры нули, а ссылочным -
значение null).

В листинге 9.8 приведен пример описания структуры, представляющей комплекс­ное число. Для экономии места из всех операций приведено только описание сложения. Обратите внимание на перегруженный метод ToString: он позволяет выводить экземпляры структуры на консоль, поскольку неявно вызывается в ме­тоде Console.WriteLine. Использованные в методе спецификаторы формата опи­саны в приложении.

Листинг 9.8. Пример структуры

using System;

namespace ConsoleApplication1

{

struct Complex

{

public double re, im;

public Complex(double re_, double im_)

{

re = re_; im = im_; // можно использовать this.re. this.im

}

public static Complex operator +(Complex a, Complex b)

{

return new Complex(a.re + b.re, a.im + b.im);

}

public override string ToString()

{

return (string.Format("({0, 2:0.##}; {1,2:0.##})", re, im));

}

class Class1

{

static void Main()

{

Complex a = new Complex(1.2345, 5.6);

Console.WriteLine("а = " + а);

Complex b;

b.re = 10; b.im = 1;

Console.WriteLine("b = " + b);

Complex с = new Complex();

Console.WriteLine("c = " + с);

с = a + b;

Console.WriteLine("c = " + с);

}

}

}

}

Результат работы программы:

а = (1.23;5.6)

b = (10; 1) с = (0; 0) с = (11.23; 6.6)

При выводе экземпляра структуры на консоль выполняется упаковка, то есть не­явное преобразование в ссылочный тип. Упаковка (это понятие было введено в разделе «Упаковка и распаковка», см. с. 36) применяется и в других случаях, когда структурный тип используется там, где ожидается ссылочный, например, при преобразовании экземпляра структуры к типу реализуемого ею интерфейса. При обратном преобразовании — из ссылочного типа в структурный — выполня­ется распаковка.

Присваивание структур имеет, что естественно, значимую семантику, то есть при присваивании создается копия значений полей. То же самое происходит и при передаче структур в качестве параметров по значению. Для экономии ре­сурсов ничто не мешает передавать структуры в методы по ссылке с помощью ключевых слов ref или out.

Особенно значительный выигрыш в эффективности можно получить, используя массивы структур вместо массивов классов. Например, для массива из 100 эк­земпляров класса создается 101 объект, а для массива структур — один объ­ект. Пример работы с массивом структур, описанных в предыдущем листинге:

Complex [] mas = new Complex[4];

for (int i = 0; i < 4; ++i)

{

mas[i].re = i;

mas[i].im = 2 * i;

}

foreach (Complex elem in mas)

Console.WriteLine(elem);

Если поместить этот фрагмент вместо тела метода Main в листинге 9.5, получим следующий результат:

(0; 0)

(1; 2)

(2; 4)

(3; 6)


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: