17

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.

Community
  • 1
  • 1
devuxer
  • 41,681
  • 47
  • 180
  • 292
  • 1
    This question seems to be off-topic, because no one can answer why the designers of .NET made a decision (unless Anders or one of the original designers happens to see this question). Therefore, it can't be definitively answered, and just opens a discussion topic. Voting to migrate it to [programmers](http://programmers.stackexchange.com). – Ken White Jan 19 '12 at 01:57
  • 1
    The .NET designers never considered structs evil so they had no reason to do this. This whole evil stuff only came later when it became common to assume that programmers had trouble understanding the difference between a value and a reference type. Outlawing i++ will be next if we follow your lead. – Hans Passant Jan 19 '12 at 02:00
  • `System.Drawing.Point` is mutable as well: http://msdn.microsoft.com/en-us/library/system.drawing.point.x.aspx – Matt Greer Jan 19 '12 at 02:01
  • 4
    Just bring out the Eric Lippert signal already. – MPelletier Jan 19 '12 at 02:02
  • 4
    @Ken, I would think there are a number of people who could hazard a reasonable guess as to why the design is the way it is (for example, http://stackoverflow.com/tags/c%23/topusers). I don't need an actual .NET architect to pipe in to get a reasonable answer, I just want to gain an understanding of the potential benefits of making these particular structs mutable. Although I don't know the answer, I don't feel this question is all that subjective. – devuxer Jan 19 '12 at 02:15
  • 2
    That's a good argument, but I'm afraid I still can't agree. The point I was making is that unless one of the original decision-makers answers (and is provably one of those people who could know), it's just an opinion being expressed (as you say, a "reasonable guess"), and therefore no single answer could definitively be selected as being correct. That makes it a discussion rather than a question/answer, and according to the [FAQ](http://stackoverflow.com/faq) it's off-topic here. (Note I didn't downvote the question; I just voted to migrate it.) – Ken White Jan 19 '12 at 02:21
  • @Ken, Perhaps it's just the way I worded the question. Does the new wording cross the threshold to on-topic? – devuxer Jan 19 '12 at 02:47
  • It seems much better to me, and seems like a reasonable question. :) Unfortunately, I can't retract the vote to migrate it (but fortunately most people will see the new version, and it'll probably be fine - if not, I'd certainly vote to reopen it as written now). :) – Ken White Jan 19 '12 at 03:05

3 Answers3

15

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.

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
  • Very interesting +1...I hadn't thought about the XAML argument. – devuxer Jan 19 '12 at 03:19
  • So why make them structs in the first place? Is it as simple as avoiding taking a performance hit for an extra layer of indirection? – Sean U Jan 19 '12 at 03:21
  • 1
    @SeanU: I don't know if I can give a satisfactory answer for that but then again, why make them classes? They're just holding data and doesn't do anything special, there is no real need to make them classes AFAIK. – Jeff Mercado Jan 19 '12 at 03:24
  • @JeffMercado FWIW, I just put together a microbenchmark that creates an array of vectors, and then normalizes them. The `class` version takes about twice as long to run as the `struct` version. – Sean U Jan 19 '12 at 03:30
  • @SeanU On similar lines, I changed a use I had for mutable structs to mutable classes and the total time for my unit tests (i.e. including those that didn't touch them) became about 12%-13% slower. Was enough to convince me I'd made the right call, though I did make the structs internal and hence less likely to bite someone in the way they behave. – Jon Hanna Jan 19 '12 at 12:24
6

Such types are mutable because, contrary to what some people might claim, mutable value-type semantics are useful. There are a few places where .net tries to pretend that value types should have the same semantics as reference types. Since mutable value-type semantics are fundamentally different from mutable reference-type semantics, pretending they're the same will cause problems. That doesn't make them "evil", however--it merely shows a flaw in an object model which assumes that acting upon a copy of something will be semantically equivalent to acting upon the original. True if the thing in question is an object reference; generally true--but with exceptions--if it's an immutable structure; false if it's a mutable structure.

One of the beautiful things about structs with exposed fields is that their semantics are readily ascertained by even simple inspection. If one has a Point[100] PointArray, one has 100 distinct instances of Point. If one says PointArray[4].X = 9;, that will change one item of PointArray and no other.

Suppose instead of using struct Point, one had a mutable class PointClass:

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

How many PointClass instances are stored in PointClass[100] PointClassArray? Is there any way to tell? Will the statement PointClass[4].X = 9 affect the value of PointClass[2].X? What about someOtherObject.somePoint.X?

While the .net collections are not well suited to storage of mutable structs, I would nonetheless regard:

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

to have relatively clear semantics, at least in the absence of threading issues. While I consider it unfortunate that .net collections don't provide a means by which one could simply say myDict[lookupKey].X = 9; I would still regard the above code as pretty clear and self-explanatory without having to know anything about Point other than the fact that it has a public integer field called X. By contrast, if one had a Dictionary<PointClass>, it would be unclear what one should be expected to do to change the X value associated with "George". Perhaps the PointClass instance associated with George is not used anywhere else, in which case one may simply write the appropriate field. On the other hand, it's also possible that someone else has grabbed a copy of MyDict["George"] for the purpose of capturing the values therein, and isn't expecting that the PointClass object he's grabbed might change.

Some people might think "Point" should be an immutable struct, but the effect of a statement like somePoint.X = 5; can be fully determined knowing only that somePoint is a variable of type Point, which in turn is a struct with a public int field called X. If Point were an immutable struct, one would have to instead say something like somePoint = new Point(5, somePoint.Y);, which would, in addition to being slower, require examining the struct to determine that all of its fields are initialized in the constructor, with X being the first and Y the second. In what sense would that be an improvement over somePoint.X = 5;?

BTW, the biggest 'gotcha' with mutable structs stems from the fact that there's no way for the system to distinguish struct methods which alter 'this' from those which do not. A major shame. The preferred workarounds are either to use functions which return new structs derived from old ones, or else use static functions which accept "ref" struct parameters.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • +1 for a thought provoking answer, but I have couple comments/questions: Regarding your example about the array of `Point` class instances, why would I actually want to know how many unique instances are in such an array? As for your argument that it would be very difficult to set one of the field values if the struct were immutable, I could counter that the struct would just need a pair of convience methods of the form `public Point ReplaceX(double x) { return new Point(x, this.Y); }`. The usage would be `ImmutablePoints[5].ReplaceX(5);`. – devuxer Jan 20 '12 at 23:00
  • Cancel my first question, I reread that paragraph and now I see your point. What you really want to know when changing a property on a class instance is if anyone else is using the same instance for another purpose. If that's an issue, though, a class can be made immutable as well. – devuxer Jan 20 '12 at 23:17
  • @DanM: If two Point instances are distinct, that does not imply that the *values* are different--merely that the instances can be modified independently of each other. One could use convenience methods of the form you describe (though the usage would be `ImmutablePoints[5]=ImmutablePoints[5].ReplaceX(5)`), but the code would be slower and the amount of information required to read and write code would go up. To ensure that the above statement would actually behave as intended, one would have to examine the definition of `ReplaceX` as well as the definition of the constructor, and... – supercat Jan 20 '12 at 23:24
  • ...*all* the backing fields of the struct. Not so bad if the struct has just two fields, but even with a four-field struct (e.g. `Rectangle`) that pattern can get annoying and can be error-prone, especially if there are any constructor variants which leave some fields as default values. If the goal is to have something that's like another object except that one field is different, it's cleaner to copy the object and change the field that to hand-copy all the fields to the new object and hope one does so correctly. – supercat Jan 20 '12 at 23:29
  • @DanM: Immutable classes and immutable structs have very similar semantics. The primary differences are that classes cannot have any non-nullable default value, classes can be more efficient than structs in cases where many most locations hold references to shared instances, and structs are generally more efficient than classes in cases where most instances would be distinct. – supercat Jan 20 '12 at 23:45
  • All good points...I still take issue with `Point.Normalize()`, though. There should at least be a `GetNormalized()` method that returns a new value for the very real possibility that I want to keep the original and normalized versions around at the same time. – devuxer Jan 21 '12 at 00:08
  • @DanM: Public struct methods, other than property settters, which modify `this` are dangerous and should generally be avoided. Likewise for struct implementations of interface methods *including property setters*. Sometimes struct interfaces may have to do such 'evil' things, but the documentation for such structs should make clear what's going on, and outside exposure of such structs should be limited as much as practical. – supercat Jan 21 '12 at 00:18
  • The point on limiting exposure is important. The smaller the range of code something is exposed to, the less potential harm an approach with these sorts of downsides can bring. On one extreme a private nested mutable struct used by one method will either work correctly or not, on the other a public mutable struct that is the cornerstone of a library's use and hence expected to be used by a lot of code will have a much greater chance of causing confusion. – Jon Hanna Jan 21 '12 at 15:08
  • @JonHanna: Having `Rectangle` as a mutable struct is far more convenient than would be having it as an immutable struct, or immutable class. Having it be a mutable class would cause no end of confusion. I think `Rectangle` is a better examine of something which *should* be a mutable structure than `Point`, since making `Point` immutable would only be a minor nuisance. Making `Rectangle` immutable would be much worse. As for `Vector.Normalize()`, I would simply consider that a poorly-designed method. – supercat Jan 22 '12 at 00:42
1

Possibilities:

  1. It seemed like a good idea at the time to someone who didn't consider the use-cases where it would bite people. List<T>.Enumerator is a mutable struct that was used because it seemed like a good idea at the time to take advantage of the micro-opts that would often happen. It's almost the poster-child for mutable structs being "evil" as it's bitten more than a few people. Still, it seemed like a good idea to someone at the time...
  2. They did think of the downsides, but had some use-case known to them where the performance differences went in struct's favour (they don't always) and was considered important.
  3. They didn't consider structs evil. "Evil" is an opinion about down-sides beating up-sides, not a demonstrable fact, and not everyone has to agree with something even if Eric Lippert and Jon Skeet say it. Personally I think they're not evil, they're just misunderstood; but then again, evil is often easier to deal with than misunderstood for a programmer, so that's actually worse... ;) Maybe those involved disagree.
Jon Hanna
  • 110,372
  • 10
  • 146
  • 251
  • It's probably worth noting that the `List.Enumerator` only really causes problems when using constrained generics or type inference. Casting the `List.Enumerable` to `IEnumerator` will turn it into a reference type. Only when it's actually used as type `List.Enumerator` does it behave as a value type; in the absence of generics or type inference, the only reason it would be declared that way would be if someone wanted to take advantage of its value semantics. I suspect `List.Enumerator` followed in the footsteps of `List.Enumerator` because doing otherwise would violate the... – supercat Jan 20 '12 at 23:35
  • ...Principle of Least Astonishment for people who were used to using `List.Enumerator`. – supercat Jan 20 '12 at 23:35
  • That, and `GetEnumerator` fits an interesting space in terms of visibility. It's formally as visible as any other member, but generally not called directly. In analysis of "how much do I need to worry about a caller doing something crazy" it falls inbetween private and most uses of public. I've more to say on this, so I think I might add an anwer to the "mutable types are evil" thread that is quite old but keeps coming up. – Jon Hanna Jan 21 '12 at 02:33
  • That ties in somewhat with a question I asked, perhaps not very well, wondering if there's any way to define types in such a way as to declaratively discourage external code from persisting them. As an example, one might define a `PointList` whose indexed property would return a struct with private fields holding a reference to the `PointList` and a subscript. That could in turn define "X" and "Y" properties that would use methods like `GetXAt()` or `SetYAt()` to allow constructs like `MyList[4].X = 9`. – supercat Jan 21 '12 at 14:18
  • @supercat Perhaps the best way to do that would be to write it in a language other than C# that would allow the return of, and use of, a managed reference, though it wouldn't be verifiable. Since you can't have fields that are managed references, and can't serialise them, it would restrict persistence. – Jon Hanna Jan 21 '12 at 14:42
  • Eric has blogged on the subject; to make it really work, what would be needed would be a 'safe managed reference' type, which would be guaranteed to be ultimately bound to a class field (such a reference could be safely cast to an ordinary managed reference, but not vice versa); the system should probably further subdivide managed references into read-write and read-only. Were I designing a framework, there would be a kind of property available with a pair of methods; one would return a 'safe managed heap reference'; code which called the first would be expected to call the second... – supercat Jan 22 '12 at 00:29
  • ...one it was done with the first, passing a parameter to indicate whether the reference may have been written to, and whether code's progress after making the reference was proceeding normally or exiting via exception; compilers would be expected to enforce the second function call automatically. An alternative would be to have a property invoke a passed-in delegate which would accept the backing field by reference. That approach could work out pretty efficiently, and make it easier to enforce the execution of post-office code, but to be most useful it would need viariadic generics. – supercat Jan 22 '12 at 00:32
  • @supercat safe reference types already exist, and indeed the type of a reference parameter is of such type (i.e. with `ref int x`, `x` is of a different type in the entire method than if it was `int x`) and you can write code that uses them. As I understand it, the issue is that the verifier currently can't detect the safety of even the most obviously safe use and they'd want us to be able to write verifiable code with it (and well, wouldn't you want that too?). – Jon Hanna Jan 22 '12 at 12:11
  • Perhaps I should have used some other terminology, but at present methods in safe code cannot return a ref, since there's no guarantee that the ref won't outlive its target. If there were a type of ref that was guaranteed to point to a field within a heap object, that type of ref could be returned safely. – supercat Jan 22 '12 at 16:01
  • @supercat stack or heap it'll have to capture something to ensure lifetime if you take that approach (which is already possible, just rather verbose). The alternative is to change the verification rules so we know (again, whether stack or heap) that the reference lives less long than the referenced. – Jon Hanna Jan 22 '12 at 17:14
  • If there were a kind of ref that were guaranteed not to live on the stack (meaning it could only be created from a class field, array element, or a variable of static duration), then safe code could return a ref of that kind. In most cases the thing to which a reference would be returned would already exist in a non-stack storage location, so there would be no need to create another capture variable. Personally, btw, I dislike the way capture variables are created, since it means that changing one line of code within a routine can change the semantics of seemingly-unrelated parts. – supercat Jan 22 '12 at 18:01
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/6942/discussion-between-jon-hanna-and-supercat) – Jon Hanna Jan 22 '12 at 18:40