Within the guts of .net, the definition for a struct containing certain members is the same as a definition for a class which those same fields and members, and which inherits from System.ValueType
. Note that compilers will not allow one to declare a class
which inherits ValueType
, but when one declares a struct
, the compiler, "behind the scenes" declares a class which does.
What makes value types special in .net is the way the run-time allocates storage locations (variables, fields, parameters, etc.) When a storage location of a type not inheriting from ValueType
is declared, the runtime will allocate space for a heap object reference. By contrast, when a storage location of a type inheriting from ValueType
is declared, the runtime will allocate space for all the public and private fields of that type. For a type like int
, the system allocates a private field which is of a special primitive type, outside the normal type system.
Note that a storage location of a value type doesn't really hold an instance of that type; instead is an instance of that type, and holds all of the fields of that type. A statement like struct1 = struct2
does not replace the value-type instance struct1
with the instance struct2
. Instead, it copies all of the fields from struct2
over the corresponding fields in struct1
. Likewise if a value-type storage location is passed as a method to a procedure without using the ref
keyword, what is passed is not the struct instance itself, but rather the contents of its fields.
If it is necessary to copy a value-type storage location to a one of type not derived from ValueType
(e.g. Object
or IComparable
), the system will create a new heap-object instance of the value type, copy all the fields from the value type to that new instancen and store a reference to that new instance in the target storage location. This process is called "boxing". Most compilers will do this implicitly, thus attempting to behave as though a value type storage location holds an object which derives from ValueType
. It's important to note, though, that this is an illusion. If type X
derives from Y
, one has an X
named xx
and a Y
named yy
, and one performs xx = yy
, such a statement should cause xx
and yy
to refer to the same object instance. That will happen if xx
and yy
are types not derived from ValueType
, even if yy
holds an instance of something derived from ValueType
. It will not happen, however, if xx
and/or yy
derives from ValueType
. In that case, the system will copy fields from one instance to another (possibly new) instance.