По суті структура - це не більше і не менше, ніж сукупність полів. У .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 # не дозволяє їм спільно використовувати такий синтаксис з масивами.