C# doesn't allow structs to derive from classes, but all ValueTypes derive from Object. Where is this distinction made?
How does the CLR handle this?
C# doesn't allow structs to derive from classes, but all ValueTypes derive from Object. Where is this distinction made?
How does the CLR handle this?
Your statement is incorrect, hence your confusion. C# does allow structs to derive from classes. All structs derive from the same class, System.ValueType, which derives from System.Object. And all enums derive from System.Enum.
UPDATE: There has been some confusion in some (now deleted) comments, which warrants clarification. I'll ask some additional questions:
Plainly yes. We can see this by reading the first page of the specification:
Now, I note that the specification overstates the case here. Pointer types do not derive from object, and the derivation relationship for interface types and type parameter types is more complex than this sketch indicates. However, plainly it is the case that all struct types derive from a base type.
Sure. A struct type can override
ToString
. What is it overriding, if not a virtual method of its base type? Therefore it must have a base type. That base type is a class.Plainly no. This does not imply that structs do not derive from a class. Structs derive from a class, and thereby inherit the heritable members of that class. In fact, structs are required to derive from a specific class: Enums are required to derive from
Enum
, structs are required to derive fromValueType
. Because these are required, the C# language forbids you from stating the derivation relationship in code.When a relationship is required, the language designer has options: (1) require the user to type the required incantation, (2) make it optional, or (3) forbid it. Each has pros and cons, and the C# language designers have chosen differently depending on the specific details of each.
For example, const fields are required to be static, but it is forbidden to say that they are because doing so is first, pointless verbiage, and second, implies that there are non-static const fields. But overloaded operators are required to be marked as static, even though the developer has no choice; it is too easy for developers to believe that an operator overload is an instance method otherwise. This overrides the concern that a user may come to believe that the "static" implies that, say "virtual" is also a possibility.
In this case, requiring a user to say that their struct derives from ValueType seems like mere excess verbiage, and it implies that the struct could derive from another type. To eliminate both these problems, C# makes it illegal to state in the code that a struct derives from a base type, though plainly it does.
Similarly all delegate types derive from
MulticastDelegate
, but C# requires you to not say that.So, now we have established that all structs in C# derive from a class.
Many people are confused by the inheritance relationship in C#. The inheritance relationship is quite straightforward: if a struct, class or delegate type D derives from a class type B then the heritable members of B are also members of D. It's as simple as that.
What does it mean with regards to inheritance when we say that a struct derives from ValueType? Simply that all the heritable members of ValueType are also members of the struct. This is how structs obtain their implementation of
ToString
, for example; it is inherited from the base class of the struct.Yes. All private members of a base class are also members of the derived type. It is illegal to call those members by name of course if the call site is not in the accessibility domain of the member. Just because you have a member does not mean you can use it!
We now continue with the original answer:
Extremely well. :-)
What makes a value type a value type is that its instances are copied by value. What makes a reference type a reference type is that its instances are copied by reference. You seem to have some belief that the inheritance relationship between value types and reference types is somehow special and unusual, but I don't understand what that belief is. Inheritance has nothing to do with how things are copied.
Look at it this way. Suppose I told you the following facts:
There are two kinds of boxes, red boxes and blue boxes.
Every red box is empty.
There are three special blue boxes called O, V and E.
O is not inside any box.
V is inside O.
E is inside V.
No other blue box is inside V.
No blue box is inside E.
Every red box is in either V or E.
Every blue box other than O is itself inside a blue box.
The blue boxes are reference types, the red boxes are value types, O is System.Object, V is System.ValueType, E is System.Enum, and the "inside" relationship is "derives from".
That's a perfectly consistent and straightforward set of rules which you could easily implement yourself, if you had a lot of cardboard and a lot of patience. Whether a box is red or blue has nothing to do with what it's inside; in the real world it is perfectly possible to put a red box inside a blue box. In the CLR, it is perfectly legal to make a value type that inherits from a reference type, so long as it is either System.ValueType or System.Enum.
So let's rephrase your question:
as
When you phrase it like that, I hope it's obvious. There's nothing stopping you from putting a red box inside box V, which is inside box O, which is blue. Why would there be?
AN ADDITIONAL UPDATE:
Joan's original question was about how it is possible that a value type derives from a reference type. My original answer did not really explain any of the mechanisms that the CLR uses to account for the fact that we have a derivation relationship between two things that have completely different representations -- namely, whether the referred-to data has an object header, a sync block, whether it owns its own storage for the purposes of garbage collection, and so on. These mechanisms are complicated, too complicated to explain in one answer. The rules of the CLR type system are quite a bit more complex than the somewhat simplified flavour of it that we see in C#, where there is not a strong distinction made between the boxed and unboxed versions of a type, for example. The introduction of generics also caused a great deal of additional complexity to be added to the CLR. Consult the CLI specification for details, paying particular attention to the rules for boxing and constrained virtual calls.
This is a somewhat artificial construct maintained by the CLR in order to allow all types to be treated as a System.Object.
Value types derive from System.Object through System.ValueType, which is where the special handling occurs (ie: the CLR handles boxing/unboxing, etc for any type deriving from ValueType).
Small correction, C# doesn't allow structs to custom derive from anything, not just classes. All a struct can do is implement an interface which is very different from derivation.
I think the best way to answer this is that
ValueType
is special. It is essentially the base class for all value types in the CLR type system. It's hard to know how to answer "how does the CLR handles this" because it's simply a rule of the CLR.So let's try this:
This will not even compile. Compiler will remind you "Type 'System.ValueType' in interface list is not an interface".
When decompile Int32 which is a struct, you will find :
public struct Int32 : IComparable, IFormattable, IConvertible {}, not mentionning it is derived from System.ValueType. But in object browser, you do find Int32 does inherit from System.ValueType.
So all these lead me to believe:
Rationale
Of all the answers, @supercat's answer comes closest to the actual answer. Since the other answers don't really answer the question, and downright make incorrect claims (for example that value types inherit from anything), I decided to answer the question.
Prologue
This answer is based on my own reverse engineering and the CLI specification.
struct
andclass
are C# keywords. As far as the CLI is concerned, all types (classes, interfaces, structs, etc.) are defined by class definitions.For example, an object type (Known in C# as
class
) is defined as follows:An interface is defined by a class definition with the
interface
semantic attribute:What about value types?
The reason that structs can inherit from
System.ValueType
and still be value types, is because.. they don't.Value types are simple data structures. Value types do not inherit from anything and they cannot implement interfaces. Value types are not subtypes of any type, and they do not have any type information. Given a memory address of a value type, it's not possible to identify what the value type represents, unlike a reference type which has type information in a hidden field.
If we imagine the following C# struct:
The following is the class definition of that struct:
So what's going on here? It clearly extends
System.ValueType
, which is an object/reference type, and implementsSystem.ICloneable
.The explanation is, that when a class definition extends
System.ValueType
it actually defines 2 things: A value type, and the value type's corresponding boxed type. The members of the class definition define the representation for both the value type and the corresponding boxed type. It is not the value type that extends and implements, it's the corresponding boxed type that does. Theextends
andimplements
keywords only apply to the boxed type.To clarify, the class definition above does 2 things:
System.ValueType
, and implementing theSystem.ICloneable
interface.Note also, that any class definition extending
System.ValueType
is also intrinsically sealed, whether thesealed
keyword is specified or not.Since value types are just simple structures, don't inherit, don't implement and don't support polymorphism, they can't be used with the rest of the type system. To work around this, on top of the value type, the CLR also defines a corresponding reference type with the same fields, known as the boxed type. So while a value type can't be passed around to methods taking an
object
, its corresponding boxed type can.Now, if you were to define a method in C# like
public static void BlaBla(MyNamespace.MyValueType x)
,you know that the method will take the value type
MyNamespace.MyValueType
.Above, we learned that the class definition that results from the
struct
keyword in C# actually defines both a value type and an object type. In C#, we can only refer to the defined value type. However, this is only a limitation of C#. In IL we can actually refer to both.When refering to types in IL, a couple of constraints are supported, among which are
class
andvaluetype
. If we usevaluetype MyNamespace.MyValueType
we're constraining the type to the value type of the class definition. Likewise, we can useclass MyNamespace.MyValueType
to constrain the type to the boxed type of the class definition.That is,
.method static void Print(valuetype MyNamespace.MyValueType test) cil managed
takes the value type defined by theMyNamespace.MyValueType
class definition,while
.method static void Print(class MyNamespace.MyValueType test) cil managed
takes the boxed type defined by theMyNamespace.MyValueType
class definition.So you can basically instantiate and use the corresponding boxed type throughout your IL program, as if it was defined like a C# class. This would initialize the valuetype
The runtime will refuse to instantiate the boxed type through newobj like
However, the
box
instruction will instantiate the boxed type of the value type and copy the values from the value type into it. That will get you an instance of the boxed type that you can store in anySystem.ValueType
,object
orclass MyNamespace.MyValueType
variable, and pass around to any method that takes aSystem.ValueType
,object
orclass MyNamespace.MyValueType
as an argument, and it will, for all intents and purposes, work like any other reference type. It is NOT a value type, but the corresponding boxed type of a value type.Example
Below is a small program I have written in IL to demonstrate this. I've commented it throughout. Pay special attention to the use of the
valuetype
andclass
keywords.As you may have noticed, the two
Print()
methods are overloads and can be, because they don't have the same signature. One takes the value type of theMyNamespace.MyValueType
class definition, and the other takes the boxed type of theMyNamespace.MyValueType
class definition.If you were to assemble the code above and decompile it using eg. ILSpy, you'd get some confusing results, even when outputting as IL, because ILSpy (can't comment on other decompilers) will assume the constraint based on the type, so even though one method takes a reference type and the other a value type, both will show up with the
valuetype
keyword. When decompiling as C#, both overload signatures will appear identical.Here's the output from ILSpy of the assembled IL code above (the comments are mine):
Don't confuse the above decompilation to C# as how to do it in C#. It's not possible, and the decompilation is very, very wrong. It's simply to illustrate how ILSpy and other decompilers can be misleading. (Just like they can be misleading when looking at things like
System.Int32
that appears to contain itself (while it in fact contains anint32
, which is a built-in type, for whichSystem.Int32
is the corresponding CLS type (And like two overloaded methods can take a value type and boxed type of the same class definition, two overloads can take anint32
andSystem.Int32
and co-exist (and ILSpy will show them both taking 'int'))).ILDasm will correctly show the
valuetype
andclass
constraint keywords.Summary
So, in summary, and to answer the question:
Value types are not reference types and do not inherit from
System.ValueType
or any other type, and they cannot implement interfaces. The corresponding boxed types that are also defined do inherit fromSystem.ValueType
and can implement interfaces.A
.class
definition defines different things depending on circumstance.interface
semantic attribute is specified, the class definition defines an interface.interface
semantic attribute is not specified, and the definition does not extendSystem.ValueType
, the class definition defines an object type (class).interface
semantic attribute is not specified, and the definition does extendSystem.ValueType
, the class definition defines a value type and its corresponding boxed type (struct).Memory layout
This section assumes a 32-bit process
As already mentioned, value types do not have type information, and thus it's not possible to identify what a value type represents from its memory location. A struct describes a simple data type, and contains just the fields it defines:
If we imagine that an instance of MyStruct was allocated at address 0x1000, then this is the memory layout:
Structs default to sequential layout. Fields are aligned on boundaries of their own size. Padding is added to satisfy this.
If we define a class in the exact same way, as:
Imagining the same address, the memory layout is as follows:
Classes default to automatic layout, and the JIT compiler will arrange them in the most optimal order. Fields are aligned on boundaries of their own size. Padding is added to satisfy this. I'm not sure why, but every class always has an additional 4 bytes at the end.
Offset 0 contains the address of the object header, which contains type information, the virtual method table, etc. This allows the runtime to identify what the data at an address represents, unlike value types.
Thus, value types do not support inheritance, interfaces nor polymorphism.
Methods
Value types do not have virtual method tables, and thus do not support polymorphism. However, their corresponding boxed type does.
When you have an instance of a struct and attempt to call a virtual method like
ToString()
defined onSystem.Object
, the runtime has to box the struct.However, if the struct overrides
ToString()
then the call will be statically bound and the runtime will callMyStruct.ToString()
without boxing and without looking in any virtual method tables (structs don't have any). For this reason, it's also able to inline theToString()
call.If the struct overrides
ToString()
and is boxed, then the call will be resolved using the virtual method table.However, remember that
ToString()
is defined in the struct, and thus operates on the struct value, so it expects a value type. The boxed type, like any other class, has an object header. If theToString()
method defined on the struct was called directly with the boxed type in thethis
pointer, when trying to access fieldA
inMyStruct
, it would access offset 0, which in the boxed type would be the object header pointer. So the boxed type has a hidden method that does the actual overriding ofToString()
. This hidden method unboxes (address calculation only, like 'unbox') the boxed type then statically calls theToString()
defined on the struct.Likewise, the boxed type has a hidden method for each implemented interface method that does the same unboxing then statically calls the method defined in the struct.
CLI specification
Boxing
Defining value types
Value types do not inherit
Value types do not implement interfaces
Referring to value types vs boxed types
Note: The specification is wrong here, there is no
boxed
keyword. The keyword isclass
.Epilogue
I think part of the confusion of how value types seem to inherit, stems from the fact that C# uses casting syntax to perform boxing and unboxing, which makes it seem like you're performing casts, which is not the case.
(object)myStruct
in C# creates a new instance of the boxed type of the value type; it does not perform any casts. Likewise,(MyStruct)obj
in C# unboxes a boxed type, copying the value part out; it does not perform any casts.A boxed value type is effectively a reference type (it walks like one and quacks like one, so effectively it is one). I would suggest that ValueType isn't really the base type of value types, but rather is the base reference type to which value types can be converted when cast to type Object. Non-boxed value types themselves are outside the object hierarchy.