Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are value types immutable by definition?

I frequently read that structs should be immutable - aren't they by definition?

Do you consider int to be immutable?

int i = 0; i = i + 123; 

Seems okay - we get a new int and assign it back to i. What about this?

i++; 

Okay, we can think of it as a shortcut.

i = i + 1; 

What about the struct Point?

Point p = new Point(1, 2); p.Offset(3, 4); 

Does this really mutate the point (1, 2)? Shouldn't we think of it as a shortcut for the following with Point.Offset() returning a new point?

p = p.Offset(3, 4); 

The background of this thought is this - how can a value type with no identity be mutable? You have to look at it at least twice to determine if it changed. But how can you do this without an identity?

I don't want to complicate reasoning about this by considering ref parameters and boxing. I am also aware that p = p.Offset(3, 4); expresses immutability much better than p.Offset(3, 4); does. But the question remains - aren't value types immutable by definition?

UPDATE

I think there are at least two concepts involved - the mutability of a variable or field and the mutability of the value of a variable.

public class Foo {     private Point point;     private readonly Point readOnlyPoint;      public Foo()     {         this.point = new Point(1, 2);         this.readOnlyPoint = new Point(1, 2);     }      public void Bar()     {         this.point = new Point(1, 2);         this.readOnlyPoint = new Point(1, 2); // Does not compile.          this.point.Offset(3, 4); // Is now (4, 6).         this.readOnlyPoint.Offset(3, 4); // Is still (1, 2).     } } 

In the example we have to fields - a mutable one and a immutable one. Because a value type field contains the whole value, a value type stored in a immutable field must be immutable, too. I am still quite suprised by the result - I did not exspect the readonly field to remain unmodified.

Variables (besides constants) are allways mutable, hence they imply no restriction on the mutability of value types.


The answer seems not to be that straight forward so I will rephrase the question.

Given the following.

public struct Foo {     public void DoStuff(whatEverArgumentsYouLike)     {         // Do what ever you like to do.     }      // Put in everything you like - fields, constants, methods, properties ... } 

Can you give a completed version of Foo and an usage example - that may include ref parameters and boxing - so that it is not possible to rewrite all occurences of

foo.DoStuff(whatEverArgumentsYouLike); 

with

foo = foo.DoStuff(whatEverArgumentsYouLike); 
like image 444
Daniel Brückner Avatar asked May 15 '09 12:05

Daniel Brückner


People also ask

What are immutable values?

Objects whose value is unchangeable once they are created are called immutable.

Which type is immutable?

An immutable type, in the context of C#, is a type of object whose data cannot be changed after its creation. An immutable type sets the property or state of the object as read only because it cannot be modified after it is assigned during initialization.

Are object values immutable?

Arrays and Objects Are MutableArrays and objects are not immutable in JavaScript because they can indeed change their value over time.

Are variables immutable?

Therefore, if a variable can change its value it is mutable in nature. Otherwise, if it cannot change its value after creation it is immutable in nature.


1 Answers

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

like image 126
Lucas Avatar answered Sep 21 '22 21:09

Lucas