I received the following question that relates to Item 7 in Effective C#. It’s not strictly an errata, so I thought I would post it here for general discussion:
In Item 7, you recommend the use of immutable structs. I am puzzled by why you would recommend structs and not immutable classes. It seems to me that one of the main benefits of immutable objects is that there is no need to copy them; you can pass references to them all around your program without any worries that they might get changed. But structs automatically get copied every time they get passed anywhere. You gain a little bit of efficiency by not having to reference them, but at the price of having to keep making more and more copies of whatever fields your struct has. It seems to me that unless you are concerned about thread safety, you might as well make a struct mutable, because it is going to get copied anyway.
You could, of course, put "ref" on all of the methods that handle your struct, but that seems like you're back to the equivalent of just using immutable reference types, and you've done it in a goofy roundabout way that is liable to confuse somebody else who has to maintain your program.
(There are other reasons I don't like using structs. They look exactly like objects, and they can be passed to methods and such that are expecting objects, which again can lead to some heavy confusion to a maintainance programmer.)
I’ll boil this down to three questions:
This is the subject of Item 6. The short answer is that classes support polymorphism, whereas structs do not. (Yes, I’m simplifying. Structs are derived from System.Object, so they do support all the methods from System.Object.) However, removing support for inheritance, virtual functions, and the other tools of object – oriented programming gives you some increased efficiency: All methods are statically bound, which increases the likelihood of inlining. In addition, there are no heap allocations, and no corresponding workload for the garbage collector.
Sometimes you don’t need the extra power of a class, so the simpler tool is the best one.
Your closing comments are all valid: structs do complicate many things in a large program. Because of that I would guess that 95% of the time, I pick a class. That 5% is important and will often make a program more efficient.
Structs should be immutable because they are simpler. Once created in a valid state, they remain so. It’s also true that a mutable struct probably has a broken GetHashCode() method, but that’s the subject of Item 10. Finally, from a design standpoint, structs usually represent a single datum: something that is replaced as a whole, rather than modified. Immutability would support that.
I never said not to use immutable classes. In fact, immutable classes are worth creating for many of the same reasons as immutable structs: they are simpler to code, their validity is easier to enforce, side-effects are less likely to introduce bugs, and the list goes on.
But, at some point, your program modifies its state. So, an item dedicated to creating immutable classes would be saying “use immutable classes, except when program efficiency means that creating a copy would be way too slow”. In many cases, that can be re-stated as “if you can support immutability for this type efficiently, it’s likely to be a good candidate for a struct.”
All of these projects are Open Source (using the Creative Commons license for content, and the MIT license for code). If you would like to contribute, visit our GitHub Repository. Or, if you have questions, comments, or ideas for improvement, please create an issue for us.