1

Every one know, when we takes Struct (Value Type) and pass to function, which wait for Object, boxing occurs.

But, struct inherits from ValueType, which inherits from Object...

Example:

ArrayList a = new ArrayList();
Point p = new Point(5,6);
a.Add(p);

In this example p boxed and added into ArrayList. But p already is Object (if you do "p is Object" you will get true). Does compiler check metadata to see all hierarchy of inheritances to see if some there is ValueType class to know if is value type and shall be allocated on stack? And, if it find in hierarchy of inheritance ValueType it do not continue see inner classes?

Example: compiler check Point metadata: from who inherits Point? TypeValue! ok, i do not continue - is value type

zzfima
  • 1,528
  • 1
  • 14
  • 21
  • I think you should focus on "by reference" and "by value". Moreover there is not any hierarchy for value types because they're all sealed. – Adriano Repetti Jun 05 '15 at 13:03
  • 2
    Value types are not "allocated on the stack". Consider an array of integers. Do you believe that all the integers in the array are allocated on the stack? If so then how can the array be returned from the method which allocated it? – Eric Lippert Jun 05 '15 at 14:39
  • Mr Eric Lippert, do You mean, because of stack is 1 mb it can not contain large arrays? It is logical... So, it boxed and saved on heap? – zzfima Jun 05 '15 at 18:29
  • 1
    No, I mean that the lifetime of an integer variable that is on the stack ends when a method returns; that's why we use the stack. Do you believe that integers that are in arrays are boxed? They are not. Do you believe that integers in an array are on the stack? They are not. Your belief that value types go on the stack is simply false, so stop believing it. – Eric Lippert Jun 06 '15 at 00:24
  • For that matter, you mention boxing. A boxed integer is by definition *not on the stack* so how can it be that integers always go on the stack? The correct belief is *variables whose lifetimes are equal to or shorter than the activation of the method can go on the short-lifetime storage pool if the compiler chooses to do so*. – Eric Lippert Jun 06 '15 at 00:24

3 Answers3

3

Consider some properties of structs, first:

  1. In C#, all structs inherit implicitly from the class System.ValueType, which in turn inherits from System.Object.
  2. In C#, a struct cannot inherit from any other struct. A direct consequence of this is that if you have an expression whose statically known type is a struct type, then the instance of that expression has a runtime type equal to the statically known type. (In simple terms, if you have an int expression, then the contained instance is definitely exactly an int.)

For runtime type checks, the situation is simple:

  • If you ask a struct whether it is an object, then the CLR looks up the relevant metadata and finds System.Object in the hierarchy, so it answers true.

For compile-time type checks, the compiler has to make some decisions :

  • If a struct expression is assigned to a value type storage location, then:
    • If the types are not the same but there is an implicit conversion defined, the conversion is invoked and then the assignment happens.
    • If there is no implicit conversion but the assignment is valid (the target struct type is the same as the source struct type), the assignment happens directly.
  • If a struct expression is assigned to a reference type storage location, then:
    • If there is an implicit conversion defined, the conversion is invoked and then the assignment happens.
    • If there is no implicit conversion but the assignment is valid (the target class is object or the target interface is an interface that the struct type implements), then a boxing operation occurs and then the assignment happens.

Notice that by storage location I mean variable, field, array slot, method argument, and so on.

So, it doesn't matter if you're assigning a struct instance to an object variable or if you're passing a struct instance as an argument to a method that expects an object expression (at least, assuming no overload resolution involved) - in both cases, the same rules apply.


(The situation is probably a little bit more complicated, but I believe the above covers the general cases.)

Theodoros Chatzigiannakis
  • 28,773
  • 8
  • 68
  • 104
1

But p already is Object (if you do "p is Object" you will get true).

That doesn't mean the value of p is already a reference.

From the C# spec 7.10.10, the is operator - just the relevant part, where D is Point here, and T is object:

  • If T is a reference type, the result is true if D and T are the same type, if D is a reference type and an implicit reference conversion from D to T exists, or if D is a value type and a boxing conversion from D to T exists.

The emphasis is mine - we're in the last situation. There's a boxing conversion from Point to object, which is why is returns true... but that doesn't mean you can convert a value of type Point to a value of type object (i.e. a reference) without boxing...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
1

struct, and all other value types are object already (Eric Lippert explains it here: How do ValueTypes derive from Object (ReferenceType) and still be ValueTypes?).

That doesn't mean struct and all other value types are reference types, they are boxed into a reference type. Hence, object and 'reference type' are not the same!

As Jon Skeet explains, implicit conversions are taken into account here too, so that is why you see this behavior.

Community
  • 1
  • 1
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325