Just to reiterate – What are the value types (structs, enums)
Value types are cut-down classes. Imagine classes that don’t support inheritance or finalizers, and you have the cut-down version: the struct. Structs are defined in the same way as classes (except with the struct keyword), and apart from the some limitations (described below) , structs can have the same rich members, including fields, methods, properties and operators.
In the previous post I have also outlined several benefits of using value types:
1) They don’t require garbage collection
2) They use less space due to alignment and having no object header
3) Even though they are copied you can still get around the issue by using the ref keyword
4) By default value types are not nulluble, but you can wrap them into Nulluble, essentially achieving the same semantics as a reference type.
However there are certain major drawbacks using value types:
1) Since value types are allocated on the stack, their values are copied. This becomes quite expensive for large value types. Microsoft recommends that value types should be no bigger than 16 bytes. Any bigger and you may sacrifice performance. Compare it to a complex object where only the pointer has to be copied.
2) Value types are intended to be immutable. Yes there are value types which are mutable, but that is an exception to the rule. I have already mentioned in the previous post that declaring value type as readonly might catch you of guard, since readonly value types cannot be modified. The compiler would issue no warning and you might spend days figuring out what the problem is.
3) Value types can only implement interfaces, so are not suitable for proper object oriented programming – they cannot extend classes and override virtual functions.
4) Large value types can cause cache misses. The biggest difference between stack and heap, is that on most CPU architectures the stack is more likely to be retained in CPU cache – and thereby avoid cache misses.
All in all, value types should be used with caution and only when value semantics are required. Garbage Collector by now is very efficient and shouldn’t stop you from using reference types. More often than not it will result in similar performance.