The structure in your heart is nothing but the aggregation of fields. In .NET it is possible that the structure “pretends” to be an object, and for each type of structure .NET implicitly determines the type of the heap object with the same fields and methods that, being the heap object, will behave like an object, a variable that contains a link to such a heap object (“boxed”) will demonstrate link semantics, but one that directly contains the structure is simply an aggregation of variables.
I think that most of the confusion between structures and classes stems from the fact that structures have two very different use cases that should have very different design principles, but MS recommendations don't distinguish between them. Sometimes a need arises for what behaves as an object; in this case, the MS recommendations are pretty reasonable, although the "16 byte limit" should be more like 24-32. Sometimes, however, aggregation of variables is necessary. The string used for this purpose should consist only of a combination of open fields and, possibly, an override of Equals , ToString and IEquatable(itsType).Equals . Structures that are used as clusters of fields are not objects and should not claim a role. In terms of structure, the value of the field should be no more or less than "the last thing written in this field." Any additional value must be determined by the client code.
For example, if the variable aggregation structure has the Minimum and Maximum members, the structure itself should not promise that Minimum <= Maximum . Code that receives such a structure as a parameter should behave as if it were passed to the individual Minimum and Maximum values. The requirement that Minimum does not exceed Maximum should be considered as a requirement that the Minimum parameter does not exceed the separately passed Maximum .
A useful example to consider is sometimes the existence of the ExposedHolder<T> class, defined as:
class ExposedHolder<T> { public T Value; ExposedHolder() { } ExposedHolder(T val) { Value = T; } }
If I have a List<ExposedHolder<someStruct>> , where someStruct is a variable aggregating structure, you can do things like myList[3].Value.someField += 7; , but providing myList[3].Value another code will give it the contents of Value , rather than giving it the ability to change it. In contrast, if you use a List<someStruct> , you must use var temp=myList[3]; temp.someField += 7; myList[3] = temp; var temp=myList[3]; temp.someField += 7; myList[3] = temp; . If you used the type of a mutable class, exposing the contents of myList[3] external code, you will need to copy all the fields to some other object. If you were to use an immutable class type or an object-style structure, you would need to create a new instance that was like myList[3] , except for someField , which was different, and then save that new instance in the list.
One more note. If you store a large number of similar things, it can be useful to store them in possibly nested arrays of structures, preferably to keep the size of each array between 1K and 64K or so. Arrays of structures are special because indexing provides a direct link to the structure inside, so you can say "a [12] .x = 5;". Although you can define objects of an array type, C # does not allow them to use this syntax with arrays.