structs should not be looked on as “cheap objects”; they have similar feature sets that are overlapping in some areas and disjoint in others. For example:
- structs can’t participate in polymorphism
- you can’t treat a struct as an instance of an interface without boxing it (caveat: “constrained call”, but that only works in some scenarios)
- a lot of library APIs won’t work well (or possibly at all) with structs – they expect mutable POCOs; you’ll probably want to use a library to get data from a database, serialize it, or render it in a UI – all of these things choke a bit with structs
- structs don’t work well with some patterns, such as tree or sibling relationships (a
Foocan’t contain aFooif it is a struct) – there are others - structs, and immutable types generally, can be awkward to work with
Also, note that until very recently (“ref returns” and “ref locals”) it was very hard to achieve some parts of “readonly structs allow you to only copy the reference”; this is now much simpler.
But frankly, in most scenarios POCOs are just easier to work with, and are fine for most application-code scenarios.
There are certainly times when structs are an amazing choice. It just isn’t every time. I would however, support the notion that if you’re going to use a struct, it should be either a readonly struct (by default) or a ref struct (if you know why you’re doing it); mutable non-ref structs are a recipe for pain.