I was having a discussion with some co-workers today about the distinction between value types and objects - the discussion centred around whether or not value types were objects. The elements adding to our confusion were that:
* value types (which are defined as structs) inherit from the System.ValueType class, which in turn inherits from System.Object, which would imply that they are in fact objects * the CLR spec isn't very clear on the matter (or at least, it wasn't to us)
* value types are not objects, because objects are 'reference type of a self-describing value [p18]' (and value types aren't reference types) * boxing is used to convert a value type to an object; this is handled completely transparently by the CLR (unlike Java for example), in that the 'boxed type cannot be directly referred to by name [p20]' (i.e. a developer can't create a value of the boxed type directly; the boxed type is in effect hidden to end users) * the types in the standard library deriving from System.ValueType (e.g. System.Int32) describes the value type (as opposed to implying a reference); however, any methods defined in the value type will be applied to the object resulting from the boxing operation (i.e. boxed reference type, as opposed to the value type), because any value type is boxed before a method defined in its type (which derives from System.ValueType) is called on it.
Is this correct?
This would mean that: * value types are not objects as per the CLR at the point of allocation, even though they inherit from System.ValueType which in turn inherits from System.Object * however, they are boxed to objects as soon as a method defined in their type is called on them, which sort of explains why System.ValueType inherits from System.Object * a class definition doesn't necessarily imply an object (as in System.ValueType, which is a class rather than a struct) * to summarize, even though value types (such as structs, which according to MSDN forbid inheritance) inherit from System.Object, they aren't in fact objects (until they get autoboxed).
> * value types (which are defined as structs) inherit from the > System.ValueType class, which in turn inherits from System.Object,
System.ValueType definies System.Object as the Base-Class.
> which would imply that they are in fact objects
Language compiles are making life easy because of auto-boxing (and unboxing), but in reality they are handled differently by the CLR.
> * value types are not objects, because objects are 'reference type of > a self-describing value [p18]' (and value types aren't reference > types)
If you (or the Runtime e.g. the Garbage-Collector) has a address of an object, it can get the type of the object(called MethodTable), because it's contained in the "Object-Header" (beside another Int32, the SyncBlock for other purposes). This is the "self-describing value".
Value-Type cannot exist "by their own" in the heap. They are eigher on the stack (or in a register), or contained within another type (eigher reference or value-type). Boxed Value-Types can exist on the heap.
> * boxing is used to convert a value type to an object; this is handled > completely transparently by the CLR (unlike Java for example), in that > the 'boxed type cannot be directly referred to by name [p20]' (i.e. a > developer can't create a value of the boxed type directly; the boxed > type is in effect hidden to end users)
In the case that you don't have two different types it's transparent. The is only System.Int32 for example, which is for the value-type and the boxed type. The rule is simple: When a Int32 lives (directly) on the Heap it's a boxed value-type, otherwise it's not a boxed value-type.
But the boxing and unboxing operations are not transparent for compiler-writers or if you write IL-Code. There is an explicit box and unbox IL-Instruction which must be used. Otherwise it's an invalid program.
> * the types in the standard library deriving from System.ValueType > (e.g. System.Int32) describes the value type (as opposed to implying a > reference); however, any methods defined in the value type will be > applied to the object resulting from the boxing operation (i.e. boxed > reference type, as opposed to the value type), because any value type > is boxed before a method defined in its type (which derives from > System.ValueType) is called on it.
IIRC Calling methods on Value-Types don't cause boxing by default.
> * value types are not objects as per the CLR at the point of > allocation, even though they inherit from System.ValueType which in > turn inherits from System.Object
yes
> * however, they are boxed to objects as soon as a method defined in > their type is called on them, which sort of explains why > System.ValueType inherits from System.Object
IIRC not if they are not called with indirection (e.g. over an interface-definition).
> * a class definition doesn't necessarily imply an object (as in > System.ValueType, which is a class rather than a struct)
System.ValueType is a kind of special definition. You won't be able to produce it "for your own".
> * to summarize, even though value types (such as structs, which > according to MSDN forbid inheritance) inherit from System.Object, they > aren't in fact objects (until they get autoboxed).
Thanks very much for your reply - that helps clear things up quite a lot.
> > * however, they are boxed to objects as soon as a method defined in > > their type is called on them, which sort of explains why > > System.ValueType inherits from System.Object
> IIRC not if they are not called with indirection (e.g. over an > interface-definition).
I think this is the relevant passage from the spec:
"Interfaces and inheritance are defined only on reference types. Thus, while a value type definition (see §8.9.7) can specify both interfaces that shall be implemented by the value type and the class (System.ValueType or System.Enum) from which it inherits, these apply only to boxed values."
That seems to confirm what you mention above - value types are autoboxed if methods defined in an interface are called on them, or if they are inherited, but not if they are called directly on the value. I think that this seems surprising to me because I always think of C# as being quite similar to Java, which has a clearer distinction between value and reference types (you can't call methods on value types in Java; in fact, you can't define new value types.)
I guess the original debate I was having with my co-workers hinges on the definition of an object - the CLR spec explicitly states that objects are reference types, but the class model of Int32 suggests that value types are also considered objects (because they inherit from System.Object.) This seems inconsistent (just like the fact that System.ValueType is a class but instances of it get allocated on the stack rather than the heap), but I suppose it's just a question of naming. If we consider objects to be something we can call methods on, then Int32s are objects; but if we consider that all operations that we can perform on Int32s must be done by methods, then Int32s aren't objects (because basic arithmetic operations on Int32s aren't done via method calls), and in that sense the CLR definition of an object seems more consistent with the traditional definition (that all operations on the entities should be method calls amongst others.) I guess that in that sense C#'s object model is a sort of hybrid of pure object orientation, because not all entities are objects.
> Thanks very much for your reply - that helps clear things up quite a > lot.
You're welcome.
> That seems to confirm what you mention above - value types are > autoboxed if methods defined in an interface are called on them, or if > they are inherited
You cannot inherit from ValueTypes, only Interfaces are allowed. This is basically based on the fact that ValueType are not self-describing, which meens that the Runtime doesn't know the Runtime-Type of a Reference.
> I think that this seems surprising to me because I always think of C# > as being quite similar to Java, ...
Such assumptions are invalid. Allthough the CLR and C# from the language perspective have something in common with Java, comcepts are different and things are implemented quite differently.
> ... which has a clearer distinction > between value and reference types (you can't call methods on value > types in Java; in fact, you can't define new value types.)
Value-Types in Java are just the "basic types" definied (like the BuildIn-Types in .NET). For Java there may be no big deal if you can't define Value-Types, but in .NET (e.g. with a strong P-Invoke Layer) this is essential.
> I guess the original debate I was having with my co-workers hinges on > the definition of an object - the CLR spec explicitly states that > objects are reference types, but the class model of Int32 suggests > that value types are also considered objects (because they inherit > from System.Object.)
It's definied as this in Metadata, but the CLR has "special logic" assigned to it, like you have discovered.
> This seems inconsistent (just like the fact that > System.ValueType is a class but instances of it get allocated on the > stack rather than the heap), but I suppose it's just a question of > naming.
I can't anwser why System.ValueType describes itself as a "class" in Metadata (maybe some CLR guy from MS can kick-in). It used to have no effect on the handling, because this Type is "hardcoded" in the CLR anyway. Maybe because it would cause a cicular reference (System.ValueType's base-class is System.ValueType), which maybe would be no big deal for the CLR itself, but for Metadata-Browsing Tools (compilers, IDE's, System.Reflection, ...). Another thing you gain from this is that the fact that System.ValueType inherits from System.Object makes it obvious that there is something like boxing and that you can assign a ValueType to an Object.