Відмінності від класів обумовлюють сферу застосування структур: типи даних, що мають невелику кількість полів, з якими зручніше працювати як із значеннями, а не як з посиланнями. Накладні витрати на динамічне виділення пам'яті для невеликих об'єктів можуть значно понизити швидкодію програми, тому їх ефективніше описувати як структури, а не як класи.
З іншого боку, передача структури в метод за значенням вимагає і додаткового часу, і додаткової пам'яті.
Синтаксис структури:
[атрибути ] [специфікатори] struct ім'я_ структури [ : інтерфейси ] тіло _структури
Специфікатори структури мають такий же сенс, як і для класу. У специфікаторів доступу допускаються тільки public, internal і private (останній - тільки для вкладених структур).
Інтерфейси, що реалізовуються структурою, перераховуються через кому. Тіло структури може складатися з констант, полів, методів, властивостей, подій, індексаторів, операцій, конструкторів і вкладених типів. Правила їх опису і використання аналогічні відповідним елементам класів, за виключенням деяких відмінностей, витікаючих із згаданих раніше:
· оскільки структури не можуть брати участь в ієрархіях, для їх елементів не можуть використовуватися специфікатори protected і protected internal;
· структури не можуть бути абстрактними (abstract), до того ж за умовчанням вони безплідні (sealed);
· методи структур не можуть бути абстрактними і віртуальними;
· перевизначатися (тобто описуватися із специфікатором override) можуть тільки методи, успадковані від базового класу object;
· параметр this інтерпретується як значення, тому його можна використовувати для посилань, але не для привласнення;
· при описі структури не можна задавати значення полів за умовчанням - це буде зроблено в конструкторі за умовчанням, створюваному автоматично (конструктор привласнює значущим полям структури нулі, а посилальним - значення null).
У лістингу 9.9 приведений приклад опису структури, що представляє комплексне число. Для економії місця зі всіх операцій приведений тільки опис складання. Зверніть увагу на перевантажений метод ToString: він дозволяє виводити екземпляри структури на консоль, оскільки неявно викликається в методі Console.WriteLine. Використані в методі специфікатори формату описані в додатку.
Лістинг 9.9. Приклад структури
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace examp63
{
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 Classl
{
static void Main()
{
Complex a = new Complex( 1.2345 , 5.6 );
Console.WriteLine( "а = " + a );
Complex b;
b.re = 10; b.im = 1;
Console.WriteLine( "b = " + b );
Complex с = new Complex();
Console.WriteLine( "c = " + с );
с = a + b;
Console.WriteLine( "с = " + с );
}
}
}
Результат роботи програми:
а = (1.23; 5.6)
b = (10; 1)
с = ( 0; 0)
с = ( 11.23;6.6)
При виведенні екземпляра структури на консоль виконується упаковка, тобто неявне перетворення в посилальний тип. При зворотному перетворенні - з посилального типу в структурний - виконується розпаковування.
Привласнення структур має, що природно, значущу семантику, тобто при привласненні створюється копія значень полів. Те ж саме відбувається і при передачі структур як параметрів за значенням. Для економії ресурсів ніщо не заважає передавати структури в методи по посиланню за допомогою ключових слів 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.9, отримаємо наступний результат:
( 0 0)
( 1 2)
( 2 4)
( 3 6)