Immutable ValueTypes

snarfblam

Mega-Ultra Chicken
Joined
Jun 10, 2003
Messages
1,832
Location
USA
User Rank
*Expert*
According to MSDN,
MSDN said:
Struct Usage Guidelines
It is recommended that you use a struct for types that meet any of the following criteria:
  • Act like primitive types.
  • Have an instance size under 16 bytes.
  • Are immutable.
  • Value semantics are desirable.
Note the item highlighted in red. My question is, why should a struct be immutable? There are cases where it would make sense, but why should this be a general rule? It doesnt seem appropriate in so many cases. Look at the DotNet classes such as Rectangle and Point. Why does Microsoft recommend that a struct be immutable? Mutability is important when manipulating objects. Consider the amount of garbage that is generated when manipulating strings. Even with value types, where there is no garbage, it makes a lot more work for the CPU when every little tweak generates a new instance and can make code messy, especially if operator overloads arent used.

The only argument I can see for making anything immutable is to help give reference types "value type" like behavior, such as the System.String class. Making value types immutable, though, actually seems to take away some value type behavior (you can change the width of a Rect, but not the month of a DateTime?).
 
Ive always had this problem, and this is why I think structs should ALWAYS be immutable:
Code:
    rects[0] = new Rectangle(20, 20);
    rects[0].Width = 10;
    // rect[0] will still have a width of 20

It makes for confusing code if you can edit a struct like this, because indexers will only return a copy of the value, and not the value itself.
 
That is not true. Look at the following console app I wrote:

C# Code[HorizontalRule]magic hidden text[/HorizontalRule]// Create Rectangle array and initialize first element
Rectangle[] rects = new Rectangle[10];
rects[0] = new Rectangle(1, 1, 1, 1);

// Display first element
Console.WriteLine(rects[0].ToString());

// Modify first element, and display again
rects[0].Width = 999;
Console.WriteLine(rects[0].ToString());
[HorizontalRule]Why are you quoting me?[/HorizontalRule]

The output is as follows:
Code:
1, 1, 1, 1
1, 1, 999, 1

What you are probably referring to is something more along the lines of:

C# Code[HorizontalRule]magic hidden text[/HorizontalRule]// Code inside a Form
this.Bounds.Width += 20;
[HorizontalRule]Why are you quoting me?[/HorizontalRule]
The above code would place the bounds on the stack, change the value, then pop it off without effect. This would be a logic error, but a very understandable one. For this reason, C# will actually not compile the code above. If you need to change only one aspect of the Bounds, in this case, the Width, then a struct is not what you need (hence, they provide a Width property for the Form class). Structs have value-type behavior. Pulling a struct from a property and modifying it without assigning it back is just like pulling an int from a property, modifying it, and not assigning it back. The code below does just that. Note how nonsensical it looks. In this light, the code above looks almost as silly to me as the code below.

C# Code[HorizontalRule]magic hidden text[/HorizontalRule]// Code inside a Form
this.Width + 20;
[HorizontalRule]Why are you quoting me?[/HorizontalRule]

If you understand how a struct works, you will quickly get over this problem. You might think it results in inconvenience or clumsy code. If this is the case, you are probably using structs where you shouldnt. Many features of C# have the potential to be abused. Structs are not unique in this aspect. Classes have a tendency to be more intuitive. In my programming, however, I have found structs to be pretty handy plenty of times (binary data manipulation, image manipulation, etc.) and they wouldnt be if they were immutable.

But, alas, Ive hardly addressed how any of this relates to immutability, so, lets answer the question: "How will making a struct immutable solve this problem?" Lets pretend that the Rectangle struct is immutable, like the string class is. You cant change part of it. You need to use a function to get a new object that represents the modification. Let us add the Rectangle.ChangeWidth function. Like the String.Replace function, it returns a new, modified object. So, now, lets try to change the Forms width:

C# Code[HorizontalRule]magic hidden text[/HorizontalRule]// I would still feel inclined to write code like the following,
// which would be bound to fail.
this.Bounds.ChangeWidth(100);

// In order for this to work, we still have to use the struct in
// unpleasant, less intuitive manner.
this.Bounds = this.Bounds.ChangeWidth(100);
[HorizontalRule]Why are you quoting me?[/HorizontalRule]
Okay, Ill admit that it looks nicer than using an intermediate variable and changing the width over three statements (read property to variable, modify variable, assign back). But this is not really a benefit of immutability. This hypothetical function does not require immutability, but rather immutability requires it. Whats more, we have now provided an option that will result in a logic error that the compiler will compile. And in the array example I gave at the top the syntax actually gets clumsier.

So, immutability forces us (rather than leaving us the option) to design structs such that they can be used with fewer statements and intermediate variables, resulting in prettier code, yet still does not make said structs foolproof. Is this the end result you hope to achieve with immutability? Of course, all this is said with the assumption that I correctly identified the potential risk you identified with mutable structs. This perceived problem is really the result of poorly used or misunderstood structs, but I dont see how immutability alleviates the problem.
 
Youre right, I dont know what I was thinking, other than possibly when inheriting from an ArrayList in the olden days (I do know for a fact that my code was messing up from something similar to above).
 
Actually, using an ArrayList, the code you posted would work as you originally stated because, like the Form.Bounds example I posted, getting an element from an ArrayList uses an indexer, which is a property, which is ultimately a function call, so you place the rect on the stack, modify it, and discard it without effect, thinking you have set its width. But, still, making the struct immutable doesnt really protect you from doing this by accident, as far as I can see. Quite the contrary, Ive seen a number of posts (and, embarrassingly, found myself confounded on several occasions by the same exact problem) where people couldnt get the String.Replace function or DateTime.AddX to work because it returns a new object rather than modifying the object on which the method was invoked.
 
Well thats just a problem with the fact that a struct has methods which seem to do actions other than generating a new one.
A better way to add a DateTime would be the + operator and having the static method String.Replace(x, y) would be more intuitive than the instance method x.Replace(y).
Struct methods and properties should simply return values to show their internal state, and use operators or static methods to operate on them. This forces you to treat every struct as you would an Int32 for example (change by assignment and operators).
 
I understand what you are saying, and it makes sense. The problem I addressed with String.Replace and DateTime.AddX is the combined result of their immutability and more importantly the poorly named/declared methods. With better naming, immutability might lead to less confusion with structs. Regardless, based solely on your argument, I still disagree with the statement that structs should be immutable.

Ultimately, your argument breaks down to the opinion that structs should be immutable to avoid issues resulting from their being misunderstood. However, I propose another solution to this confusion: programmers should be educated as to how their programming language works. Problem solved. Easier said than done, I know. There is no way to make all C# programmers aware of how structs work as a result of copy-on-assign behavior and stack operations. I see it as more of a road bump that all programmers should hit, and then get over it. You run into the problem, figure it out (via reference material, FAQ, message board, whatever), and the problem is solved. If structs are only used where they belong and they are designed to be used as a struct should be used then the problem they present will be minimal. And while I support the seemingly more difficult school of thought, I do so for the sake of being able to use structs to their fullest potential.

When used properly, structs are great for binary data access and can be more resource efficient. Microsoft didnt just make "another kind of class" for the hell of it, and structs work the way they do for a reason. Except, perhaps, for a few rare circumstances, if you find that your struct needs to be immutable to work intuitively then you should be using a class. If you are thinking of an instance of a struct as an object (despite the fact that they derive from System.Object) your should be using a class. Thinking in terms of objects is, I think, where this confusion comes from. A struct really isnt an object. It is a value (probably a composite value, but a value none the less).
 
Many structs probably should have been classes, for example a Rectangle, which may be one because of the need for speed.
I think the only problem is that "Production Level" structs CAN be used for such purposes. imo there should be an unsafe-type keyword for allowing an instance method or property to change the inner values of the struct, or simply allowing internal or private structs to use this behavior.
Structs and Value types add a kind of beauty to the language, when everything can be used in a similar fashion and has a place in C# type land, while removing those annoying special cases (for example, primitives in java). Mutability ruins this illusion.
 
Someone borrowed my copy of Effective C#. I believe the author had a very good description of structs and exactly why theyre immutable, and why any value types you create SHOULD be designed to be immutable.

With 50 tips in the book, Ive obviously forgotten the reason why :)

If I can find my copy, Ill try and write up the summary of what he presented.

-ner
 
Misinterpreted guidelines

The MSDN guideline you posted does not specify that structures should be immutable - it specifies that structures are recommended for storing immutable types:

It is recommended that you use a struct for types that meet any of the following criteria

That means that if you have a type that is small and acts like a primitive type then it should probably be a structure - regardless of immutability.

Think about it - if a type is immutable then there is no reason it could not work as well as a structure than as a class. And by being a value type it saves overhead regarding the GC. Therefore immutable types should probably be value types - quite different from saying value types should probably be immutable.
 
Re: Misinterpreted guidelines

The MSDN guideline you posted does not specify that structures should be immutable - it specifies that structures are recommended for storing immutable types.
Quite right. I cant believe that you are the first person to catch that. However, there are plenty of people (as you can see on this thread) that think that structs should be immutable.

Think about it - if a type is immutable then there is no reason it could not work as well as a structure than as a class.
This is probably generally true, but there are certainly cases where it isnt. If the object is large it should probably be a class to avoid large copy-on-assign operations. Luckily for us, the only objects that can possibly vary in size are strings and arrays; all other types are fixed in size. This removes most of the guesswork, making it easier to determine whether a class or struct would be appropriate.


IceAzul, I think treating structs as some sort of unsafe object would be making a mountain of a mole hill. With pointers, bad things can happen. With structs, a minor logic error can occur if you arent particularly familiar with them. As far as mutable structs ruining fairy tale Type Land, I dont think this is the case.

Structs should be used for a type of object that needs to be handled a certain way. The alternative, a struct that needs to be programmed a different way, is hardly more unified. In my programming, though, I find the availability of stack-allocated, copy-on-assign, mutable objects very important. Perhaps the reason that so many people disagree is because they dont spend so much time getting their hands dirty with binary/raw data.


Nerseus, Im just guessing here, but the reason you are looking for is probably the one I mentioned in my second post, which would be the confusion caused by manipulating a value on the stack and thinking you are manipulating data within a class.
 
Back
Top