System.Valuetype Understanding

System.ValueType Understanding

ValueType is a little white lie.

The built-in numeric types (int, long, byte), char, enums and structs are all value types.

This means that they have a different concepts of identity and equivalence to object types. If I do x = y and x and y are reference types, then x and y now point to precisely the same object. However, if I do x = y and x and y are value types, then x and y are now two completely different objects that happen to be identical. (This is also reflected in == and Equals, though that can be overridden).

(This is where people get sidetracked by talking about the stack and the heap, if they haven't already, really that's an implementation detail and while important, is not the point of the distinction between value and reference types).

Now, mostly this is all and good but one thing about reference types is that they all benefit from inheriting from System.Object. The value type int doesn't really, and that's good too as it's much better in many ways that it just be four bytes of memory handled by the lovely CPU instructions that are so good at doing so. Still, it's sometimes useful to be able to treat an int as if it also inherited from System.Object, and so you can.

Of course, this means that you may do things with int that only make sense to do on a System.Object, so when you do so the int is "boxed" and can then be "unboxed" again.

This is great, but what if we wanted to do something specific to value types? More to the point, what if the designers of the CLR did (in particular, they wanted a GetHashCode for value types that related to the value-based equivalence descibed above, rather than the identity-based equivalence that objects have)?

For this purpose we have ValueType. The system treats all value types as inheriting from this class, which in turn inherits from Object. Enum in turn inherits from value type and all enum types inherit from it, allowing some common functionality across all enums.

So, if you ever want to treat a superclass of all value types, use ValueType, but if you want to actually create a value type, create a struct or an enum as appropriate.


The Common Type System explanation:

A structure is a value type that derives implicitly from System.ValueType, which in turn is derived from System.Object. A structure is very useful for representing values whose memory requirements are small, and for passing values as by-value parameters to methods that have strongly typed parameters. In the .NET Framework class library, all primitive data types (Boolean, Byte, Char, DateTime, Decimal, Double, Int16, Int32, Int64, SByte, Single, UInt16, UInt32, and UInt64) are defined as structures.

Like classes, structures define both data (the fields of the structure) and the operations that can be performed on that data (the methods of the structure). This means that you can call methods on structures, including the virtual methods defined on the System.Object and System.ValueType classes, and any methods defined on the value type itself. In other words, structures can have fields, properties, and events, as well as static and nonstatic methods. You can create instances of structures, pass them as parameters, store them as local variables, or store them in a field of another value type or reference type. Structures can also implement interfaces.

Value types also differ from classes in several respects. First, although they implicitly inherit from System.ValueType, they cannot directly inherit from any type. Similarly, all value types are sealed, which means that no other type can be derived from them. They also do not require constructors.

For each value type, the common language runtime supplies a corresponding boxed type, which is a class that has the same state and behavior as the value type. An instance of a value type is boxed when it is passed to a method that accepts a parameter of type System.Object. It is unboxed (that is, converted from an instance of a class back to an instance of a value type) when control returns from a method call that accepts a value type as a by-reference parameter. Some languages require that you use special syntax when the boxed type is required; others automatically use the boxed type when it is needed. When you define a value type, you are defining both the boxed and the unboxed type.

The strangeness of ValueType is to allow the above to happen.

Is an instance of System.ValueType on heap or stack?

System.ValueType is not a value-type. Value-types are things that inherit from ValueType, but not ValueType itself. So:

System.ValueType foo = 5; 

here, 5 is loaded on the stack. This is then boxed (with a box of type int) onto the heap. The object reference is then assigned to the reference foo. We can see this by looking at the IL for that:

ldc.i4.5 // push int32 value 5 onto the stack
box int32 // box the value at the top of the stack, type int32
stloc.0 // assign to "foo"

In general, though: structs can be either on the heap or on the stack, depending on the context. Frankly, it doesn't matter which - because both are implementation details. It is the behaviour that matters... and (at least in their unboxed form), the key point about structs is their copy-on-assign semantic.

ValueType class vs. value types

This is a surprisingly complex area!

  • no, a.ToString() is not boxing, but this is only true so long as the value-type has an override for the method being called; for this reason, it is a good idea to always override the standard methods: ToString, Equals and GetHashCode on structs; it is theoretically possible that the JIT could do extra work here to devirtualize the calls, but: why risk it?
  • in general, things that would cause boxing include:
    • calling a non-overridden method
    • casting the struct to an interface that it implements
    • casting it to object or ValueType or Enum
  • note that "constrained call" can be used to avoid the interface boxing problem - rather than Foo(IBar) consider Foo<T>(T) where T : IBar - the latter uses constrained call, allowing access to methods etc from IBar without having to box

So: a value-type is treated as a value-type so long as it is declared and accessed as that value-type, and the methods used are declared / overridden on that value type; or When using generics as T which is a value-type when used, nothing that constrained call can provide access to methods of generics

What makes ValueType class Special?

I understand that error but what I couldn't understand is what makes ValueType class special?

The class is documented as being special. That's what makes it special.

how the compiler is informed in this case?

The compiler writers read the documentation before they write the compiler.

Does the compiler directly check whether the class is ValueType or Enum when I try to create a class that inherit from a class?

Yes.

Also all structs implicitly inherit from ValueType, but Enum class Explicitly inherit from ValueType, so how is that working?

It's working pretty well.

Are all of these special cases hard coded into the compiler?

Yes.

Isn't it more appropriate to create an attribute to specify that this class is special and cannot be inherited instead of hard coding?

No, it's not. That would imply that a third party could also make a special type that needed special handling by the compiler when inherited from. How would the third party then modify the compiler to implement those rules?

how do Value Types in .NET actually work?

The System.ValueType class is really just a "meta" class, and internally is handled differently than normal classes. Structs and primitive types inherit System.ValueType implicitly, but that type doesn't actually exist during runtime, it's just a way for assemblies to mark that a class should be treated like a value type struct and take on pass-by-value semantics.

Contrary to the other answers, value types are not always allocated on the stack. When they are fields in a class, they sit on the heap just like the rest of the class data when that object is instantiated. Local variables can also be hoisted into an implicit class when used inside iterators or closures.

Cannot convert from object to System.ValueType

That code only works when you pass it a value type. The object you're trying to pass is a reference type, so the compiler forbids you to use that as an argument:

MSDN: System.ValueType

[...] can be used as a parameter in method calls to restrict possible arguments to value types instead of all objects

So you should just change the parameter type from ValueType to object, which would make this code work for any argument type and still execute the desired behavior.

But from the variables and code it looks like you're trying to do something else entirely. Are you looking for decimal.TryParse()?

Does System.ValueType inherit from System.Object or no?

Everything inherits from object.

Even your classes such as this:

public class MyClass
{
}

follow this rule, MyClass still inherits from object. Its no different for .NET types.

How do ValueTypes derive from Object (ReferenceType) and still be ValueTypes?

C# doesn't allow structs to derive from classes

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:

Do structs derive from a base type?

Plainly yes. We can see this by reading the first page of the specification:

All C# types, including primitive types such as int and double, inherit from a single root object type.

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.

Are there other ways that we know that 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.

May I derive a user-defined struct from a class of my choice?

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 from ValueType. Because these are required, the C# language forbids you from stating the derivation relationship in code.

Why forbid it?

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.

What is the relationship between inheritance and derivation 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.

All heritable members? Surely not. Are private members heritable?

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:


How does the CLR handle this?

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:

How do ValueTypes derive from Object (ReferenceType) and still be ValueTypes?

as

How is it possible that every red box (value types) is inside (derives from) box O (System.Object), which is a blue box (a reference Type) and still be a red box (a value type)?

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.



Related Topics



Leave a reply



Submit