Структура в своей основе - не что иное, как совокупность полей.В .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
- агрегатная переменнаяstruct, можно делать что-то вроде 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
, который отличался, и затем сохранить этот новый экземпляр в списке.
Еще одно примечание: если вы храните большое количество похожих вещей, может быть целесообразно хранить их в возможно вложенных массивах структур, предпочтительно пытаясь сохранить размер каждого массива в диапазоне от 1 КБ до 64 КБ.или так.Массивы структур являются особыми, так как при индексировании можно получить прямую ссылку на структуру внутри, так что можно сказать «a [12] .x = 5;».Хотя можно определять объекты, подобные массиву, C # не позволяет им использовать такой синтаксис для массивов.