Структуры с открытыми полями не являются злом. В то время как некоторые действительно старые компиляторы будут принимать код вроде:
List<Point> myList;
myList[4].X = 5;
, который копировал бы myList [4] во временную структуру, изменял поле X этой временной структуры, а затем выбрасывал измененную структуру, а создание неизменяемых структур было способом, гарантирующим, что компилятор закричит на вышеуказанную конструкцию лучший способ гарантировать, что компилятор будет кричать при таком коде, - это изменить компилятор, чтобы запретить запись в поля временных структур. Учитывая, что такой код долгое время запрещался компиляторами, структуры с открытыми полями часто являются лучшим способом хранения вещей, которые имеют фиксированное количество независимых элементов данных (например, Point, Rectangle и т. Д.)
Проблемными являются структуры, в которых функции, отличные от конструкторов или установщиков свойств, модифицируют this
. Хотя компиляторы распознают, что запись в someStructProperty.someField
пытается изменить someStructProperty
, и запретят это в тех случаях, когда такое изменение фактически не работает, к сожалению, у компилятора нет средств, чтобы узнать, что someStructProperty.MutatingFunction()
попытается изменить временный экземпляр структуры. Таким образом, компилятор разрешит такой код, даже если он не сможет работать так, как задумано. Что бы я предложил в качестве средства исправления в случаях, когда функция должна изменять структуру «на месте», было бы определение статического метода, который принимает экземпляр структуры в качестве параметра ref
. Например, SetPointXY(ref Point pt, int x, int y)
. Компилятор будет рассматривать передачу структуры как параметр ref
как попытку изменить эту структуру и разрешит ее только в ситуациях, когда она действительно будет работать.
Обратите внимание, что с точки зрения производительности, единственный раз, когда написание структурных полей по отдельности не было бы самым быстрым способом обновления структуры, был бы, когда большая часть структуры соответствовала либо значению по умолчанию, либо некоторым другим существующим ранее структура. Могут быть такие случаи, когда было бы быстрее переписать структуру с уже существующим экземпляром, а затем записать поля, которые должны содержать что-то другое, но в целом я бы предложил использовать методы или функции для обновления структур только тогда, когда код, который делает это, будет более читабельным, чем код, который устанавливает поля индивидуально. Если AreaCode
является открытым полем типа PhoneNumber
, эффекты somePhoneNumber.AreaCode = "847";
гораздо более очевидны, чем эффекты somePhoneNumber = new PhoneNumber("847", somePhoneNumber.Exchange, somePhoneNumber.Number, somePhoneNumber.Extension);
. Среди прочего, нужно было бы изучить всю структуру, чтобы узнать, изменит ли последняя какие-либо поля, кроме AreaCode
. С другой стороны, если цель на самом деле состояла в том, чтобы иметь структуру, которая была бы пустой, за исключением некоторых совершенно новых данных, использование конструктора или метода фабрики могло бы помочь прояснить тот факт, что это делается; если просто переписать все поля структуры по отдельности, нужно будет изучить всю структуру, чтобы знать, что не было полей, которые можно было бы оставить без изменений.