Класи і структури
Структура – це окремий випадок класу. Історично структури використовуються в мовах програмування раніше класів. У мовах PL/1, C, Pascal вони були тільки сукупністю даних (полів класу), але не включали ні методів, ні подій. У мові С++ можливості структур були істотно розширені, і вони стали справжніми класами, хоча і з деякими обмеженнями. У мові C# збережений саме такий підхід до структур.
Чим слід керуватися, роблячи вибір між структурою і класом? Можна скористатися наступними правилами:
якщо необхідно віднести клас до розгорненого типу, робіть його структурою;
якщо в класі число полів відносно невелике, а число можливих об'єктів відносно велике, робіть його структурою. В цьому випадку пам'ять об'єктам відводитиметься в стеку, не створюватимуться зайві посилання, що дозволить підвищити ефективність використання пам'яті;
у решті випадків проектуйте справжні класи.
Оскільки на структури накладаються додаткові обмеження, може виникнути необхідність в компромісі – погодитися з обмеженнями і використовувати структуру або пожертвувати разгорнутістью і ефективністю і працювати із справжнім класом. Варто відзначити: коли мова йде, що вбудовані типи – int, а інші – є класами, то, насправді, мова йде про класи, реалізованих у вигляді структур.
Структури
Розглянемо тепер детальніше питання опису структур, їх синтаксису, семантики і тих особливостей, що відрізняють їх від класів.
Синтаксис структур
Синтаксис оголошення структури аналогічний синтаксису оголошення класу:
[атрибути][модифікатори]struct ім’я_структури [:список_інтерфейсів]
{тіло_структури}
Які зміни відбулися в синтаксисі заголовка структури порівняно з синтаксисом класу? Їх небагато:
ключове слово class змінене на слово struct ;
список батьків, який міг включати ім'я батьківського класу, замінений списком, що допускає тільки імена інтерфейсів. Для структур не може бути заданий батько (клас або структура). Відмітьте, структура може успадковувати інтерфейси;
для структур не застосовні модифікатори abstract і sealed. Причиною є відсутність механізму спадкоємства.
Все, що може бути вкладене в тіло класу, може бути вкладено і в тіло структури: поля, методи, конструктори та інше, включаючи класи і інтерфейси.
Аналогічно класу, структура може мати статичні і нестатичні поля і методи, може мати декілька конструкторів, зокрема статичні і закриті конструктори. Для структур можна створювати власні константи, використовуючи поля з атрибутом readonly і статичний конструктор. Структури схожі на класи по своєму опису і поводяться схожим чином, хоча і мають істотні відмінності в семантиці привласнення і перевірці еквівалентності.
Розглянемо обмеження, що накладаються на структури.
Структури і спадкоємство
Найсерйозніше обмеження — пов'язане з обмеженням спадкоємства. Для структури не може бути заданий батьківський клас або батьківська структура. У структури не може бути спадкоємців. Звичайно, всяка структура, як і будь-який клас в C#, є спадкоємцем класу object, успадковувавши всі властивості і методи цього класу. Структура може успадковувати один або декілька інтерфейсів, реалізовуючи методи цих інтерфейсів.
З чим пов'язано таке серйозне обмеження. Одне з пояснень полягає в наступному. Розглянемо привласнення
у = x;
При привласненні допустимо, щоб у належав батьківському класу, а x – його нащадкові. При посилальному привласненні жодних проблем не виникає. В результаті привласнення посилання у указуватиме на об'єкт нащадка, який крім полів батька може мати додаткові поля. Головне, що всі поля батька будуть визначені. Після такого привласнення можливо і зворотне привласнення з приведенням типу
x = (T)y;
Для структур, що належать до значущого типу, подібне привласнення між батьком і нащадком було б неможливим. Батьку не можна привласнити об'єкт нащадка, оскільки у батька менше полів, чим у нащадка, тому можна було б привласнити лише частину полів. А вже у зворотний бік операція привласнення була б принципово неможлива. І це одна з причин, по якій зважилися відмовитися від спадкоємства для значущих типів в мові C#.
