Search code examples
c#.netstructimmutabilitymutable

Why are System.Windows.Point & System.Windows.Vector mutable?


Given that mutable structs are generally regarded as evil (e.g., Why are mutable structs “evil”?), are there potential benefits that might have prompted the designers of the .NET framework to make System.Windows.Point & System.Windows.Vector mutable?

I'd like to understand this so I can decide whether it would make sense to make my own similar structs mutable (if ever). It's possible the decision to make Point and Vector mutable was just an error in judgment, but if there was a good reason (e.g., a performance benefit), I'd like to understand what it was.

I know that I've stumbled over the implementation of the Vector.Normalize() method a few times because it, surprise (!), does not return a fresh Vector. It just alters the current vector.

I always think it should work like this:

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

But it actually works like this:

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

...so, it seems like immutability is a good idea simply for avoiding confusion, but again, perhaps it's worth that potential confusion in some cases.


Solution

  • These types are in the System.Windows namespace and are generally used in WPF applications. The XAML markup of an application is a big part of the framework so for a lot of things, they need a way to be expressed using XAML. Unfortunately there's no way to invoke non-parameterless constructors using WPF XAML (but it is possible in loose XAML) so trying to call a constructor with the appropriate arguments to initialize it wouldn't be possible. You can only set the values of the object's properties so naturally, these properties needed to be mutable.

    Is this a bad thing? For these types, I'd say no. They are just for holding data, nothing more. If you wanted to get the size a Window wanted to be, you'd access the DesiredSize to get the Size object representing the size it wanted. You're not meant to "change the desired size" by altering the Width or Height properties of the Size object you get, you change the size by providing a new Size object. Looking at it this way is a lot more natural I believe.

    If these objects were more complex and did more complicated operations or had state, then yes, you wouldn't want to make these types neither mutable nor structs. However since they're just about as simple and basic as it can get (essentially a POD), structs would be appropriate here.