13

I have two questions:

  1. We know all types derive from Object which is a reference type. My question is why int - which is a value type - inherits from a reference type Object? Is this possible?

  2. If int is derived from Object, why do we need to box when passing int to function which expects object as parameter? Normally with references when you need to pass object of derived type as parameter to function expecting object of base type, you don't need to do anything additional. Why box here?

For me this situation seems like a issue with how this type hierarchy was designed.

PS. I found this related question but the answer there doesn't give any realistic insight - just abstractly talks about boxes.

Community
  • 1
  • 1
  • `all types derive from Object` __isn't always true. – Amit Kumar Ghosh Dec 28 '15 at 12:30
  • 2
    the accepted answer to [this other question](http://stackoverflow.com/questions/1682231/how-do-valuetypes-derive-from-object-referencetype-and-still-be-valuetypes) is much more relevant – Paolo Falabella Dec 28 '15 at 12:34
  • 1
    [Not everything derives from object](http://i1.blogs.msdn.com/b/ericlippert/archive/2009/08/06/not-everything-derives-from-object.aspx) – Soner Gönül Dec 28 '15 at 12:34
  • 1
    Your question already has the answer. It is boxing that creates the *illusion* that value types derive from Object. – Hans Passant Dec 28 '15 at 13:18
  • 1
    @HansPassant The documentation explicitly states that value types inherit from object - which is reference type, isn't it? –  Dec 28 '15 at 13:20
  • 2
    What's wrong with an illusion? If it quacks like duck then the documentation will say it is a duck. – Hans Passant Dec 28 '15 at 13:23
  • @HansPassant Sorry but I don't understand your comment :( –  Dec 28 '15 at 13:24
  • Well, this was my question as well, when i learned about difference between value and reference types. The thing is that even though `ValueType` technically ***is*** derived from object - it has different set of rules applied to it. The memory for struct is allocated in stack whilst for object it is allocated on heap. `ValueType` is of structural type that inherits from object but compiler works with it differently. – Fabjan Dec 28 '15 at 13:36
  • It seems that the C# type system is ill-defined on its value types. In a theoretical viewpoint, value types cannot derive from reference types. It simply does not make sense. Nevertheless, it works in practice. So, it is like some hack and magic. In designing C#, Microsoft has simply decided to give up some theoretical correctness in exchange for greater convenience and easiness of use (so that all types in C# can be treated as objects). – zhoudu Jun 20 '22 at 03:49

3 Answers3

5

We need to be careful not to mix up concepts here.

First, there's subtyping. int is a subtype of object. Subtype basically means that the contract guaranteed by supertype (e.g. "There is a method ToString, which returns a suitable string repesentation.") is also guaranteed for the subtype.

Then there's inheritance in C#. In C#, inheritance

  1. creates a subtype by ensuring that the interface provided by the supertype is also available in the subtype and

  2. provides default implementations, i.e., if you do not override a method, you get the implementation of the supertype. This is basically a convenience feature.

(Interface implementation in C# would be an example for another subtyping mechanism, which provides 1 but not 2.)

That's basically all. Neither subtyping or inheritance makes any guarantee about memory layouts, value/reference type semantics, etc. The concepts are orthogonal.


"But that's not right," you might say. "Part of the object contract is 'reference type semantics'." This is where boxing is needed. It simulates reference type semantics whenever the compile-time type of a value type is a reference type (i.e. object, ValueType or an interface).

Heinzi
  • 167,459
  • 57
  • 363
  • 519
  • "But that's not right," you might say. "Part of the object contract is 'reference type semantics'." This is where boxing is needed. It simulates reference type semantics whenever the compile-time type of a value type is a reference type (i.e. object, ValueType or an interface)." - well but then normally you don't need to do anything when passing object of derived type as parameter to function which expects reference to base type. But here you need boxing. So this is strange situation to some degree –  Dec 28 '15 at 14:29
  • @user200312: Indeed. I guess it's the price we have to pay for having a common base class for both structs and classes, and for having consistent (reference type) semantics for interface-typed variables. Since the compiler does all the work (automatic boxing and unboxing), it's a price I'll gladly pay. – Heinzi Dec 28 '15 at 14:42
3

We know all types derive from Object. which is reference type. My question is why int - which is value type - inherits from a reference type Object? Is this possible?

System.Int32 derives from System.ValueType, as do all structs in C#. This inheritance chain is allowed by the compiler, which is the same mechanism which prohibits you from inheritance in any other struct type. The common language runtime (CLR) has special semantics for types that derive from System.ValueType. System.ValueType in itself isn't a value type, it's a reference type which forms the base class for all structs. Though this inheritance hierarchy exists , it need not guarantee anything about how objects will be layed out in memory.

why we need to box when passing int to function which expects object as parameter? Normally with references when you need to pass object of derived type as parameter to function expecting object of base type, you don't need to do anything additional. Why box here?

Because although any struct ultimately derives from object, it is actually treated differently by the run-time. All structs are treated as a blob of data, they have no method table pointer or sync block index, which every reference type in .NET has. That is why any value type that is passed to a method accepting an object has to be boxed, because the additional data needs to be added to it in order to actually become a fully qualified object type. Value Types aren't only boxed when they're passed as object types, they are also boxed, for example, when you call a method which was added to your struct as a result of implementing an interface. That value type needs to boxed in order to get the actual method table pointer to the method it needs to invoke.

You can see it with a small example:

void Main()
{
    IFoo m = new M();
    m.X();
}

public struct M : IFoo
{
    public void X() { }
}

public interface IFoo 
{
    void X();
}

Will yield the following IL (compiled in Release mode):

IL_0000:  ldloca.s    00 
IL_0002:  initobj     UserQuery.M
IL_0008:  ldloc.0     
IL_0009:  box         UserQuery.M
IL_000E:  callvirt    UserQuery+IFoo.X
IL_0013:  ret         
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
  • @downvoter Do explain the downvote. – Yuval Itzchakov Dec 28 '15 at 14:59
  • We all got one. Every post here. – Patrick Hofman Dec 28 '15 at 15:23
  • @Henk I did not say downvotes need to be explained. I kindly asked whoever downvoted, if he is truly kind, it explain what is wrong with the answer so I can improve it. I did not obligate anyone to do so. – Yuval Itzchakov Dec 28 '15 at 18:58
  • 1
    @HenkHolterman, while it's true that downvotes don't *have* to be explained, it's still rude to downvote without explaining why. But of course, being rude isn't forbidden... – Thomas Levesque Dec 28 '15 at 21:14
  • 1
    Hi again! Remember I said I'm learning from you? lol This is interesting, so tried myself but you need to clarify one more thing! When I tested with `M m = new M();` instead of `IFoo m = new M();`, boxing didn't happen. – Jenix Feb 21 '18 at 19:13
  • Oh, I think the line causes boxing whether I'm calling the method or not. – Jenix Feb 21 '18 at 19:17
1

Value types are either stack-allocated or allocated inline in a structure. Reference types are heap-allocated. Both reference and value types are derived from the ultimate base class Object. In cases where a value type needs to act like an object, a wrapper that makes the value type look like a reference object is allocated on the heap, and the value type's value is copied into it. The wrapper is marked so that the system knows that it contains a value type. This process is known as boxing, and the reverse process is known as unboxing. Boxing and unboxing allow any type to be treated as an object.

Surya
  • 173
  • 4