An object is immutable if its state
doesn’t change once the object has
been created.
Short answer: No, value types are not immutable by definition. Both structs and classes can be either mutable or immutable. All four combinations are possible. If a struct or class has non-readonly public fields, public properties with setters, or methods which set private fields, it is mutable because you can change its state without creating a new instance of that type.
Long answer: First of all, the question of immutability only applies to structs or classes with fields or properties. The most basic types (numbers, strings, and null) are inherently immutable because there is nothing (field/property) to change about them. A 5 is a 5 is a 5. Any operation on the 5 only returns another immutable value.
You can create mutable structs such as System.Drawing.Point
. Both X
and Y
have setters which modify the struct's fields:
Point p = new Point(0, 0);
p.X = 5;
// we modify the struct through property setter X
// still the same Point instance, but its state has changed
// it's property X is now 5
Some people seem to confuse immutablity with the fact that value types are passed by value (hence their name) and not by reference.
void Main()
{
Point p1 = new Point(0, 0);
SetX(p1, 5);
Console.WriteLine(p1.ToString());
}
void SetX(Point p2, int value)
{
p2.X = value;
}
In this case Console.WriteLine()
writes "{X=0,Y=0}
". Here p1
was not modified because SetX()
modified p2
which is a copy of p1
. This happens because p1
is a value type, not because it is immutable (it isn't).
Why should value types be immutable? Lots of reasons... See this question. Mostly it's because mutable value types lead to all sorts of not-so-obvious bugs. In the above example the programmer might have expected p1
to be (5, 0)
after calling SetX()
. Or imagine sorting by a value which can later change. Then your sorted collection will no longer be sorted as expected. The same goes for dictionaries and hashes. The Fabulous Eric Lippert (blog) has written a whole series about immutability and why he believes it's the future of C#. Here's one of his examples that lets you "modify" a read-only variable.
UPDATE: your example with:
this.readOnlyPoint.Offset(3, 4); // Is still (1, 2).
is exactly the what Lippert referred to in his post about modifying read-only variables. Offset(3,4)
actually modified a Point
, but it was a copy of readOnlyPoint
, and it was never assigned to anything, so it's lost.
And that is why mutable value types are evil: They let you think you are modifying something, when sometimes you are actually modifying a copy, which leads to unexpected bugs. If Point
was immutable, Offset()
would have to return a new Point
, and you would not have been able to assign it to readOnlyPoint
. And then you go "Oh right, it's read-only for a reason. Why was I trying to change it? Good thing the compiler stopped me now."
UPDATE: About your rephrased request... I think I know what you're getting at. In a way, you can "think" of structs as being internally immutable, that modifying a struct is that same as replacing it with a modified copy. It might even be what the CLR does internally in memory, for all I know. (That's how flash memory works. You cannot edit just a few bytes, you need to read a whole block of kilobytes into memory, modify the few you want, and write the whole block back.) However, even if they were "internally immutable", that is an implementation detail and for us developers as users of structs (their interface or API, if you will), they can be changed. We can't ignore that fact and "think of them as immutable".
In a comment you said "you cannot have a reference to the value of field or variable". You are assuming that every struct variable has a different copy, such that modifying one copy does not affect the others. That is not entirely true. The lines marked below are not replaceable if...
interface IFoo { DoStuff(); }
struct Foo : IFoo { /* ... */ }
IFoo otherFoo = new Foo();
IFoo foo = otherFoo;
foo.DoStuff(whatEverArgumentsYouLike); // line #1
foo = foo.DoStuff(whatEverArgumentsYouLike); // line #2
Lines #1 and #2 do not have the same results... Why? Because foo
and otherFoo
refer to the same boxed instance of Foo. Whatever is changed in foo
in line #1 reflects in otherFoo
. Line #2 replaces foo
with a new value and does nothing to otherFoo
(assuming that DoStuff()
returns a new IFoo
instance and does not modify foo
itself).
Foo foo1 = new Foo(); // creates first instance
Foo foo2 = foo1; // create a copy (2nd instance)
IFoo foo3 = foo2; // no copy here! foo2 and foo3 refer to same instance
Modifying foo1
won't affect foo2
or foo3
. Modifying foo2
will reflect in foo3
, but not in foo1
. Modifying foo3
will reflect in foo2
but not in foo1
.
Confusing? Stick to immutable value types and you eliminate the urge of modifying any of them.
UPDATE: fixed typo in first code sample