Почему System.Windows.Point & System.Windows.Vector изменчивы? - PullRequest
16 голосов
/ 19 января 2012

Учитывая, что изменяемые структуры обычно считаются злом (например, Почему изменяемые структуры являются «злыми»? ), существуют ли потенциальные преимущества, которые могли бы побудить разработчиков платформы .NET сделать System.Windows.Point & System.Windows.Vector изменчивый?

Я хотел бы понять это, чтобы я мог решить, имеет ли смысл сделать мои собственные подобные структуры изменяемыми (если вообще когда-либо). Возможно, решение сделать изменяемыми Point и Vector было просто ошибкой в ​​суждении, но если бы была веская причина (например, выигрыш в производительности), я бы хотел понять, что это было.

Я знаю, что я несколько раз сталкивался с реализацией метода Vector.Normalize(), потому что он, к удивлению (!), Не возвращает новый Vector. Это просто изменяет текущий вектор.

Я всегда думаю, что это должно работать так:

var vector = new Vector(7, 11);
var normalizedVector = vector.Normalize(); // Bzzz! Won't compile

Но на самом деле это работает так:

var vector = new Vector(7, 11);
vector.Normalize(); // This compiles, but now I've overwritten my original vector

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

Ответы [ 3 ]

13 голосов
/ 19 января 2012

Эти типы находятся в пространстве имен System.Windows и обычно используются в приложениях WPF. XAML-разметка приложения является большой частью фреймворка, поэтому для многих вещей им нужен способ выражения с использованием XAML. К сожалению, нет способа вызвать конструкторы без параметров с использованием WPF XAML (но это возможно в свободном XAML), поэтому попытка вызвать конструктор с соответствующими аргументами для инициализации будет невозможна. Вы можете устанавливать значения свойств объекта только так, чтобы эти свойства были изменяемыми.

Это плохо? Для этих типов я бы сказал, нет. Они только для хранения данных, не более того. Если вы хотите получить размер, который должен быть Window, вы получите доступ к DesiredSize, чтобы получить объект Size, представляющий требуемый размер. Вы не должны «изменять желаемый размер», изменяя свойства Width или Height получаемого объекта Size, вы изменяете размер, предоставляя новый объект Size. Я считаю, что смотреть на это гораздо естественнее.

Если бы эти объекты были более сложными и выполняли более сложные операции или имели состояние, тогда да, вы бы не хотели делать эти типы ни изменяемыми, ни структурами. Однако, поскольку они настолько просты и просты, насколько это возможно (по сути, POD), здесь подойдет структура.

4 голосов
/ 21 января 2012

Такие типы являются изменяемыми, потому что, вопреки тому, что некоторые люди могут утверждать, изменяемая семантика типа значения полезна .В некоторых местах .net пытается сделать вид, что типы значений должны иметь ту же семантику, что и ссылочные типы.Поскольку изменчивая семантика типа значения принципиально отличается от изменчивой семантики ссылочного типа, притворство, что они одинаковые, вызовет проблемы.Однако это не делает их «злыми» - это просто показывает недостаток в объектной модели, который предполагает, что действие на копию чего-либо будет семантически эквивалентно действию на оригинал.True, если рассматриваемая вещь является ссылкой на объект;в целом верно - но с исключениями - если это неизменная структура;false, если это изменяемая структура.

Одна из замечательных особенностей структур с открытыми полями заключается в том, что их семантика легко устанавливается даже при простом осмотре.Если у человека есть Point[100] PointArray, у него есть 100 различных экземпляров Point.Если кто-то скажет PointArray[4].X = 9;, это изменит один пункт PointArray, а не другой.

Предположим, что вместо использования struct Point один имел изменяемый класс PointClass:

class PointClass {public int X; public int Y;};

Сколько экземпляров PointClass хранится в PointClass[100] PointClassArray?Есть ли способ сказать?Повлияет ли оператор PointClass[4].X = 9 на значение PointClass [2] .X?Как насчет someOtherObject.somePoint.X?

Хотя коллекции .net не очень хорошо подходят для хранения изменяемых структур, я, тем не менее, считаю, что

Dictionary<string, Point>;
...
  Point temp = myDict["George"];
  temp.X = 9;
  myDict["George"] = temp;

имеет относительно четкую семантику, по крайней мере, вотсутствие проблем с многопоточностью.Несмотря на то, что я сожалею, что коллекции .net не предоставляют средства, с помощью которого можно просто сказать myDict[lookupKey].X = 9;, я все равно расценил бы приведенный выше код как довольно ясный и не требующий пояснений без необходимости знать что-либо оКроме того, укажите, что у него есть открытое целочисленное поле с именем X. Напротив, если бы у него было Dictionary<PointClass>, было бы неясно, что нужно делать, чтобы изменить значение X, связанное с «Джорджем».Возможно, экземпляр PointClass, связанный с Джорджем, больше нигде не используется, и в этом случае можно просто написать соответствующее поле.С другой стороны, также возможно, что кто-то другой захватил копию MyDict["George"] с целью получения значений в ней, и не ожидает, что объект PointClass, который он захватил, может измениться.

Некоторые люди могут подумать, что «точка» должна быть неизменной структурой, но эффект такого выражения, как somePoint.X = 5;, может быть полностью определен, зная только, что somePoint - это переменная типа Point, которая, в свою очередь, является структурой спубличное поле int называется X.Если бы Point была неизменной структурой, вместо этого нужно было бы сказать что-то вроде somePoint = new Point(5, somePoint.Y);, что, помимо того, что было бы медленнее, потребовало бы проверки структуры, чтобы определить, что все ее поля инициализируются в конструкторе, причем Xпервое и Y второе.В каком смысле это будет улучшение по сравнению с somePoint.X = 5;?

Кстати, самая большая «ошибка» с изменяемыми структурами проистекает из того факта, что система не может отличить методы структуры, которые изменяют «это» отте, которые нет.Главный позор.Предпочтительными обходными путями являются либо использование функций, которые возвращают новые структуры, полученные из старых, либо использование статических функций, которые принимают параметры структуры «ref».

1 голос
/ 19 января 2012

Возможности:

  1. В то время это казалось хорошей идеей для тех, кто не рассматривал варианты использования, когда это кусало людей. List<T>.Enumerator - изменчивая структура, которая использовалась, потому что в то время казалось хорошей идеей воспользоваться преимуществами микроопций, которые часто бывают. Это почти ребенок-плакат для изменчивых структур, являющихся "злыми", поскольку это укушено больше чем несколькими людьми. Тем не менее, в то время это казалось хорошей идеей ...
  2. Они думали о недостатках, но им был известен некоторый вариант использования, в котором различия в производительности шли в пользу структуры (они не всегда) и считались важными.
  3. Они не считали структуры злом. «Зло» - это мнение о том, что нижние стороны бьются вверх, а не очевидный факт, и не все должны с чем-то соглашаться, даже если это скажут Эрик Липперт и Джон Скит. Лично я думаю, что они не злые, их просто неправильно поняли; но опять же, со злом часто легче иметь дело, чем с неправильно понятым для программиста, так что на самом деле это еще хуже ...;) Может быть, те, кто занимается, не согласны.
...