По суті структура - це не більше і не менше, ніж сукупність полів. У .NET структура може "прикидатися" об'єктом, і для кожного типу структури .NET неявно визначає тип об'єкта купи з тими ж полями і методами, які - будучи об'єктом купи - будуть вести себе як об'єкт . Змінна, яка містить посилання на такий об'єкт купи ("boxed" структура), буде демонструвати референтну семантику, але та, яка містить структуру безпосередньо, - це просто агрегація змінних.
Я думаю, що велика плутанина між структурами та класом випливає з того, що в структурах є два дуже різні випадки використання, які повинні мати дуже різні керівні принципи проектування, але інструкції щодо MS не розрізняють їх. Іноді виникає потреба в чомусь, що поводиться як предмет; у такому випадку вказівки щодо MS є досить розумними, хоча, "обмеження 16 байтів", мабуть, має більше нагадувати 24-32. Однак іноді потрібно лише об’єднання змінних. Структура, яка використовується для цієї мети, повинна просто складатися з масиву загальнодоступних полів, і, можливо, Equals
перевизначення, ToString
переопределення таIEquatable(itsType).Equals
впровадження. Структури, які використовуються як агрегація полів, не є об'єктами і не повинні претендувати на їх. З точки зору структури, значення поля повинно бути не більш-менш, ніж "останнє, що написано в це поле". Будь-яке додаткове значення повинно визначатися кодом клієнта.
Наприклад, якщо структура, що агрегує змінну, має члени Minimum
і Maximum
, сама структура не повинна це обіцяти Minimum <= Maximum
. Код, який отримує таку структуру як параметр, повинен вести себе так, ніби він переданий окремо Minimum
і Maximum
значеннями. Вимога, яка Minimum
не Maximum
повинна бути більшою, ніж повинна розглядатися як вимога, щоб Minimum
параметр не був більшим, ніж окремо переданий Maximum
.
Корисна модель, яку слід враховувати іноді, - це ExposedHolder<T>
визначити клас таким чином:
class ExposedHolder<T>
{
public T Value;
ExposedHolder() { }
ExposedHolder(T val) { Value = T; }
}
Якщо є структура List<ExposedHolder<someStruct>>
, де someStruct
структура, що агрегує змінну, вона може робити такі речі myList[3].Value.someField += 7;
, але myList[3].Value
якщо інший код надасть їй зміст, Value
а не надасть йому спосіб змінити його. На противагу цьому, якщо хтось використовував a List<someStruct>
, його потрібно було б використовувати var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Якщо використовується тип мутаційного класу, для викриття вмісту myList[3]
зовнішньому коду потрібно буде скопіювати всі поля до якогось іншого об'єкта. Якщо хтось використовував тип непорушного класу або структуру "об'єктного стилю", потрібно було б сконструювати новий екземпляр, який був подібний, myList[3]
за винятком someField
якого був іншим, а потім зберегти цей новий екземпляр у списку.
Ще одна примітка: Якщо ви зберігаєте велику кількість подібних речей, можливо, буде добре зберігати їх у можливо вкладених масивах структур, бажано намагаючись зберегти розмір кожного масиву між 1 К і 64 К або більше. Масиви структур є особливими, оскільки індексація дає пряме посилання на структуру всередині, тому можна сказати "a [12] .x = 5;". Хоча можна визначити об’єкти, схожі на масив, C # не дозволяє їм спільно використовувати такий синтаксис з масивами.