Почему System.Drawing Rectangle, Point, Size и т. д. являются изменяемыми структурами, а не классами?

Есть ли причина, по которой Microsoft решила создать эти структуры?

Все три изменяемы. Мне было бы намного проще иметь с ними дело, если бы они были либо неизменяемыми, либо ссылочными типами.

Если есть причины, по которым они должны быть структурами, почему они изменяемы?


person Flash    schedule 07.01.2013    source источник
comment
Не могли бы вы уточнить, как их изменчивая structness вызывает у вас проблемы?   -  person Damien_The_Unbeliever    schedule 07.01.2013
comment
@Damien_The_Unbeliever, например: foreach (Point r in points) { r.X += 1; } ‹-- не работает. По сути, я ожидаю, что они будут вести себя как ссылочные типы, но постоянно должен напоминать себе, что это не так.   -  person Flash    schedule 07.01.2013
comment
Связанный, возможный дубликат: Почему System.Windows.Point & System .Windows.Vector изменяемый?   -  person sloth    schedule 07.01.2013
comment
@DominicKexel, вопрос связан, хотя принятый ответ относится только к WPF.   -  person Rotem    schedule 07.01.2013
comment
@Rotem Вот почему я не голосовал за закрытие :-)   -  person sloth    schedule 07.01.2013
comment
@Andrew Ваш пример кода не работает только потому, что вы используете foreach, который упаковывает значения. Он работает в простом цикле for. Все три структуры полностью изменяемы.   -  person Rotem    schedule 07.01.2013
comment
@Rotem Да, но мне кажется, что это должно работать, потому что r.X += 1 подразумевает, что r имеет ссылочную семантику. Другой пример: если мне разрешено делать r.X += x, мне должно быть позволено заставить работать void offsetX(Point p, int xOffset), не прибегая к ref. Я могу заставить его работать, зная, что это тип значения, но почему Microsoft должна меня смущать?   -  person Flash    schedule 07.01.2013
comment
Тот факт, что r.X += 1 работает, означает только то, что это поле доступно для записи, а не то, что это значение или ссылочный тип. В вашем методе вы должны использовать ref, потому что в противном случае вы будете изменять копию Point, а это не то, что вам нужно.   -  person Rotem    schedule 07.01.2013


Ответы (2)


Почему они являются структурами

Семантика значений
Между двумя идентичными экземплярами этих значений нет существенной разницы. Любая Point с координатами, [2,3] равна любой другой точке с теми же координатами, как равны любые две int с одинаковым значением. Это соответствует руководству по проектированию:

Он логически представляет одиночное значение, подобное примитивным типам (целое число, двойное число и т. д.).

Производительность

Типы значений дешевле выделять и освобождать.

Часто требуется создать множество экземпляров этих значений. Создание структур обходится дешевле, и если они являются локальными значениями, они будут создаваться в стеке, снимая нагрузку со стороны сборщика мусора.

Размер
Рассмотрим размер этих значений:
Point : 8 байт
Size: 8 байт
Rectangle: 16 байт

Для Point и Size их размер такой же, как у ссылки на экземпляр класса в 64-битной системе.

Цитаты взяты из рекомендаций Microsoft: Выбор между Классы и структуры

Почему они изменчивы

Эти структуры полностью изменяемы. Это делается (вопреки рекомендациям) ради производительности, поскольку позволяет избежать необходимости создавать новые значения для операций модификации.

Относительно примера кода OP в комментариях:

Point[] points = new Point[] { new Point(0,0), new Point(1,1), new Point(2,2) };

foreach (Point p in points)
{
    p.X += 1; 
}

Единственная причина, по которой этот foreach терпит неудачу, заключается в том, что p упакован в object, чтобы обеспечить итерацию, и вы Cannot modify the result of an unboxing conversion, (спасибо Раджив), итератор возвращает данные по значению, и вы будете вносить изменения только в копию значения.

Это отлично работает:

for (int i = 0; i < points.Length; i++)
{
    points[i].X += 1;
}
person Rotem    schedule 07.01.2013
comment
@Rotem Rectangle нарушает три из четырех правил Microsoft по этой ссылке. - person Flash; 07.01.2013
comment
@Андрей согласен! Но я предполагаю, что тот факт, что это значение логически перевешивает другие пункты. - person Rotem; 07.01.2013
comment
@Rotem Я понимаю твою точку зрения. Может быть, вопрос должен быть, почему они изменчивы? - person Flash; 07.01.2013
comment
@JLRishe Нет, они изменчивы. Например, Rectangle имеет метод Offset для изменения своих координат. В-третьих, для меня Rectangle не представляет собой единственное значение, но я полагаю, что это субъективно :) - person Flash; 07.01.2013
comment
@ Андрей Моя ошибка. В этом случае я тоже нахожу странным, что они изменчивы. Я отозвал свой предыдущий комментарий. - person JLRishe; 07.01.2013
comment
@Andrew: Мне очень не нравится, как написано это руководство по единым ценностям. Изменяемая структура — это тип данных, который лучше всего инкапсулирует группу независимых, но связанных значений, без инкапсуляции коллективного идентификатора. Значения в неизменяемом типе гораздо сложнее использовать независимо, чем в структуре открытого поля, а открытые ссылки на изменяемые объекты не могут избежать инкапсуляции идентичности. Чем выше независимость членов типа, тем сильнее необходимость того, чтобы он был структурой, а не классом. - person supercat; 05.10.2013
comment
@Rotem, я думаю, компилятор переведет цикл foreach в Point[] в обычный цикл for, верно? Потому что он делает это для int[]. Так почему же точка попадет в коробку? Я что-то упустил здесь? - person Rajeev; 06.07.2017
comment
@ Раджив, я не понимаю, что ты имеешь в виду. Вам определенно не разрешено изменять значение переменной итерации в foreeach, например. foreach (int i in myInts) i += 1;. Это приводит к ошибке компилятора. - person Rotem; 06.07.2017
comment
@Rotem, у меня просто есть сомнения относительно вашего утверждения Единственная причина, по которой этот foreach терпит неудачу, заключается в том, что p упаковывается в объект, чтобы обеспечить итерацию, зачем здесь нужна упаковка? - person Rajeev; 06.07.2017
comment
@Rajeev Я думаю, вы правы, он будет упакован только в том случае, если контейнер не является универсальным. Я не знаю, на основании какой информации я написал ответ, это было более 4 лет назад. Тем не менее, foreach по-прежнему невозможен, потому что итератор возвращает копию данных. Я изменил ответ, чтобы отразить эту информацию. - person Rotem; 06.07.2017

Microsoft не нужно определять эти структуры как класс.

В основном это небольшие конструкции.

  • Rectangle Structure хранит набор из четырех целых чисел.
  • Point Structure представляет упорядоченную пару целых чисел x- и y -координаты.
  • Size Structure хранит упорядоченную пару целых чисел.

Если они определены как class, для структуры Point одни и те же координаты могут относиться к разным объектам в памяти. Определяя как struct, мы знаем, что между разными точками с одинаковыми координатами нет разницы. Это означает, что они являются типами значений. Типы значений почти всегда дешевле выделять. Посмотрите на их размер;

Point : 8 bytes
Size: 8 bytes
Rectangle: 16 bytes

Кто хочет выделять новую часть памяти каждый раз, когда они создают Point(1,2)?

person Soner Gönül    schedule 07.01.2013