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.