Структуры с открытыми изменяемыми полями или свойствами не являются злом.
Методы структуры (в отличие от установщиков свойств), которые изменяют «это», являются чем-то злым, только потому, что .net не предоставляет средства отличить их от методов, которые этого не делают. Методы структуры, которые не изменяют «this», должны вызываться даже для структур только для чтения без необходимости защитного копирования. Методы, которые изменяют "this", вообще не должны вызываться в структурах только для чтения. Поскольку .net не хочет запрещать использование структурных методов, которые не изменяют «this», для структур, доступных только для чтения, но не хочет разрешать изменение структур, доступных только для чтения, он защищенно копирует структуры в режиме чтения. только контексты, возможно получая худшее из обоих миров.
Тем не менее, несмотря на проблемы с обработкой самопутывающихся методов в контекстах только для чтения, изменяемые структуры часто предлагают семантику, намного превосходящую изменяемые типы классов. Рассмотрим следующие три сигнатуры метода:
struct PointyStruct {public int x, y, z;};
class PointyClass {public int x, y, z;};
void Method1 (PointyStruct foo);
void Method2 (ref PointyStruct foo);
void Method3 (PointyClass foo);
Для каждого метода ответьте на следующие вопросы:
- Предполагая, что метод не использует «небезопасный» код, может ли он изменить foo?
- Если до вызова метода не существует внешних ссылок на 'foo', может ли существовать внешняя ссылка после?
ответы:
Вопрос 1::
Method1()
нет (ясное намерение)
Method2()
: да (ясное намерение)
Method3()
: да (неопределенное намерение)
Вопрос 2
Method1()
:: нет
Method2()
: нет (если небезопасно)
Method3()
: да
Method1 не может изменить foo и никогда не получает ссылку. Method2 получает кратковременную ссылку на foo, которую он может использовать для изменения полей foo любое количество раз в любом порядке, пока не вернется, но он не может сохранить эту ссылку. Перед возвратом Method2, если он не использует небезопасный код, все копии, которые могли быть сделаны из его ссылки 'foo', исчезнут. Метод 3, в отличие от метода 2, получает беспорядочную ссылку на foo, и никто не знает, что он может с этим делать. Это может вообще не изменить foo, это может изменить foo и затем вернуться, или это может дать ссылку на foo другому потоку, который может каким-то образом изменить его в некоторый произвольный момент времени в будущем.
Массивы структур предлагают прекрасную семантику. Учитывая RectArray [500] типа Rectangle, становится ясным и очевидным, как, например, скопировать элемент 123 в элемент 456, а затем через некоторое время установить ширину элемента 123 в 555, не нарушая элемент 456. "RectArray [432] = RectArray [321 ]; ...; RectArray [123] .Width = 555; ". Знание того, что Rectangle - это структура с целочисленным полем Width, расскажет все, что нужно знать о приведенных выше утверждениях.
Теперь предположим, что RectClass был классом с теми же полями, что и Rectangle, и один хотел выполнить те же операции над RectClassArray [500] типа RectClass. Возможно, массив должен содержать 500 предварительно инициализированных неизменяемых ссылок на изменяемые объекты RectClass. в этом случае правильный код будет выглядеть примерно так: «RectClassArray [321] .SetBounds (RectClassArray [456]); ...; RectClassArray [321] .X = 555;». Возможно, предполагается, что массив содержит экземпляры, которые не будут изменяться, поэтому правильный код будет больше похож на "RectClassArray [321] = RectClassArray [456]; ...; RectClassArray [321] = New RectClass (RectClassArray [321 ]); RectClassArray [321] .X = 555; " Чтобы знать, что нужно делать, нужно знать гораздо больше о RectClass (например, поддерживает ли он конструктор копирования, метод копирования и т. Д.). ) и предполагаемое использование массива. Нигде не так чисто, как использование структуры.
Безусловно, для любого класса контейнера, кроме массива, нет хорошего способа предложить чистую семантику массива структуры. Лучшее, что можно было бы сделать, если бы кто-то хотел, чтобы коллекция была проиндексирована, например, строкой, вероятно, было бы предложить универсальный метод ActOnItem, который бы принимал строку для индекса, универсальный параметр и делегат, который был бы передан. по ссылке и общий параметр, и элемент коллекции. Это позволило бы использовать почти ту же семантику, что и структурные массивы, но если люди vb.net и C # не смогут предложить хороший синтаксис, код будет выглядеть неуклюже, даже если он имеет разумную производительность (передача универсального параметра разрешить использование статического делегата и избежать необходимости создавать какие-либо временные экземпляры классов).
Лично меня раздражает ненависть Эрика Липперта и других. выбросить в отношении изменяемых типов значений. Они предлагают намного более чистую семантику, чем беспорядочные ссылочные типы, которые используются повсеместно. Несмотря на некоторые ограничения, связанные с поддержкой .net для типов значений, во многих случаях изменяемые типы значений лучше подходят, чем любой другой тип объектов.
int
s,bool
s и все другие типы значений являются злом. Есть случаи изменчивости и неизменности. Эти случаи зависят от роли, которую играют данные, а не от типа распределения / распределения памяти.