Структура в своем сердце не более и не меньше, чем агрегация полей. В .NET возможно, что структура «притворяется» объектом, и для каждого типа структуры .NET неявно определяет тип объекта кучи с теми же полями и методами, которые - будучи объектом кучи - будут вести себя как объект , Переменная, которая содержит ссылку на такой объект кучи («в штучной упаковке»), будет демонстрировать ссылочную семантику, но тот, который непосредственно содержит структуру, представляет собой просто агрегацию переменных.
Я думаю, что большая часть беспорядка между структурами и классами проистекает из того факта, что структуры имеют два очень разных варианта использования, которые должны иметь очень разные принципы проектирования, но рекомендации 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
, а не дайте ему средство его изменения. В отличие от этого, если использовать List<someStruct>
, необходимо использовать var temp=myList[3]; temp.someField += 7; myList[3] = temp;
. Если использовать тип изменяемого класса, то для отображения содержимого myList[3]
внешнему коду потребуется скопировать все поля на какой-либо другой объект. Если бы использовался тип неизменяемого класса или структура «объект-стиль», было бы необходимо создать новый экземпляр, который был бы как myList[3]
, за исключением someField
, который был другим, а затем сохранил этот новый экземпляр в списке.
Еще одно примечание. Если вы храните большое количество похожих вещей, может быть полезно сохранить их в возможно-вложенных массивах структур, предпочтительно, чтобы сохранить размер каждого массива между 1K и 64K или около того. Массивы структур являются особенными, поскольку индексирование дает прямую ссылку на структуру внутри, поэтому можно сказать «a [12] .x = 5;». Хотя можно определить объекты типа массива, C# не позволяет им использовать такой синтаксис с массивами.
Есть ли у них только публичные поля, или у них также будут методы? Являются ли типы примитивными типами, например целыми? Будут ли они содержаться в массиве или что-то вроде List? –
JeffFerguson
Если у вас есть сомнения, используйте класс. Если вам нужна автоматическая инициализация в массиве, используйте struct. – leppie
Список изменчивых структур? Следите за велоцираптором. –