44

It's not possible to inherit from a C# struct. It's not obvious to me why this is:

  • Clearly you can't have a reference type that inherits from a value type; this wouldn't work
  • It doesn't sound reasonable to inherit from one the primitive types (Int32, Double, Char, etc.)
  • You'd need to be able to call (non-virtual) methods on the base using a derived instance. You could cast from a derived struct to the base, since they would overlap the same memory. I guess casting from a base to derived wouldn't work, since you couldn't know the type of the derived struct at runtime.
  • I can see that you couldn't implement virtual methods in your class hierarchy, since value types can't have virtual members

I wonder if this is a technical limitation in the CLR, or something that the C# compiler stops you from doing?

Edit: Value types can't have virtual methods, and I realise this limitation rules out most scenarios in which you'd want to use inheritance. That still leaves inheritance-as-aggregation, though. Imagine a Shape struct with a Colour field: I can write code that accepts any struct derived from Shape, and access its Colour field, even if I can never write a virtual Shape.Draw method.

I can think of one scenario that would get broken by non-sealed value types. Value types are supposed to implement Equals and GetHashCode correctly; even though these two methods on System.Object are virtual, they get called non-virtually on value types. Even if value types weren't sealed, someone writing a struct derived from another one couldn't write their own implementation of these two methods and expect to have them called correctly.

I should point out that I'm not suggesting I should be able to inherit from structs in my own code. What I am trying to do, though, is to guess why this particular code smell is forbidden by .NET.

Edit 2: I just spotted this very similar question, the answer to which is effectively "because then arrays of value types wouldn't work".

Community
  • 1
  • 1
Tim Robinson
  • 53,480
  • 10
  • 121
  • 138
  • possible duplicate of [Why don't structs support inheritance?](http://stackoverflow.com/questions/1222935/why-dont-structs-support-inheritance) – nawfal May 27 '13 at 05:40

4 Answers4

59

The reason is that most inheritance techniques relate to runtime polymorphism (virtual functions) and those don’t work on value types: for runtime polymorphism to have any meaning, objects need to be treated as references – this isn’t specific to .NET either, it’s simply a technical detail of how virtual functions are implemented.

Value types form an exception to .NET’s rule, precisely to allow lightweight objects that don’t require indirection via references. So runtime polymorphism doesn’t work for them and most aspects of inheritance become meaningless.

(There’s an exception: a value type object can be boxed, which allows for virtual methods inherited from System.Object to be called.)

To address one of your points:

  • You could cast from a derived struct to the base, since they would overlap the same memory.

No, this would not be possible – casting a value type would copy its value. We’re not dealing with references here, so no overlap in memory. Casting a value type to its base type is therefore meaningless (again, unless we’re talking about conversion to object which actually performs boxing under the hood, and also operates on a copy of the value).

Still not clear? Let’s look at an example.

Let’s say we’ve got the hypothetical struct Shape and, inheriting from it, the struct Circle. Shape defines a virtual Draw method (which accepts a Graphics object). Now, let’s say we want to draw a shape on a canvas. This, of course, works perfectly well:

var circle = new Circle(new Point(10, 10), 20);
circle.Draw(e.Graphics); // e.Graphics = graphics object of our form.

– But here we don’t actually use inheritance at all. To make use of inheritance, imagine instead the following DrawObject helper method:

void DrawObject(Shape shape, Graphics g) {
    // Do some preparation on g.
    shape.Draw(g);
}

And we call it elsewhere with a Circle:

var circle = new Circle(new Point(10, 10), 20);
DrawObject(circle, e.Graphics);

– And, ka-blam – this code doesn’t draw a circle. Why? Because when we pass the circle to the DrawObject method, we do two things:

  • We copy it.
  • We slice it, i.e. the object shape object is really no longer a Circle – neither the original one nor a copy. Instead, its Circle portion was “sliced” away during copying and only the Shape portion remains. shape.Draw now calls the Draw method of Shape, not of Circle.

In C++, you can actually cause this behaviour. For that reason, OOP in C++ only works on pointers and references, not on value types directly. And for that same reason, .NET only allows inheritance of reference types because you couldn’t use it for value types anyway.

Notice that the above code does work in .NET if Shape is an interface. In other words, a reference type. Now the situation is different: your circle object will still be copied but it will also be boxed into a reference.

Now, .NET could theoretically allow you to inherit a struct from a class. Then the above code would work just as well as if Shape were an interface. But then, the whole advantage of having a struct in the first place vanishes: for all intents and purposes (except for local variables which never get passed to another method, hence no utility of inheritance) your struct would behave as an immutable reference type instead of a value type.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
  • Re "casting a value type would copy its value" - in the IL, a method on a struct accepts a pointer to the struct, not a copy of it, so calling methods on the base could still work. For instance, to compile "42.ToString()", the compiler must allocate a temporary local, put the number 42 in it, and take its address. – Tim Robinson Nov 20 '09 at 09:36
  • @Tim: you never call virtual methods on local objects, that doesn’t make any sense. You only do this on values passed in via parameters (or the likes) and *there* you will get a (sliced) copy instead of a reference. – Konrad Rudolph Nov 20 '09 at 10:28
  • "Shape defines a virtual Draw method" - isn't going to work anyway, because value types don't have virtual methods. Without virtual methods, inheritance is limited, but not useless. – Tim Robinson Nov 20 '09 at 10:53
  • @Tim: No virtual methods? The do too: have a look at `Equals`. – Anyway, you’re only talking about code inheritance instead of interface inheritance then. Code inheritance can be trivially implemented using composition and the consensus in the OO community is that you should (almost?) always favour composition over inheritance when it comes to code reuse, since this is actually an implementation detail. What other uses of inheritance have I forgotten? – Konrad Rudolph Nov 20 '09 at 11:58
  • I understand why inheritance on value types, and inheritance in general, is a code smell. I know why I try to avoid it in my own code, and why I wouldn't want to write derived value types even if I could. What I'm after is: what technical reason does the CLR have to prohibit inheritance on value types? – Tim Robinson Nov 20 '09 at 12:39
  • @Tim: more precisely, a valuetype is boxed when you call an 'instance' method of it. – leppie Nov 20 '09 at 12:49
  • @leppie: I don’t actually believe that. Why would it? It’s definitely not necessary, only for virtual method calls. – Konrad Rudolph Nov 20 '09 at 13:02
  • @KonradRudolph: Defining a struct effectively creates two types: a value type, and a class type which holds an instance of that value type and regards that instance's fields as its own. The class type overrides some `Object` virtual methods, but the value type itself doesn't have any virtual methods. Note that `GetType` isn't a virtual method, but it requires boxing since it examines hidden "type information" data which only exists for class instances. – supercat Aug 17 '12 at 19:31
  • @supercat Quite right, I simplified here (but see the third paragraph of my answer …). – Konrad Rudolph Aug 20 '12 at 09:23
  • @KonradRudolph: I'd suggest rewording that paragraph. Although only boxed structures support virtual function calls, the system can call methods like `ToString` directly on structures without virtual dispatch and without boxing. Using a struct's override of `Object.Equals` to compare structures typically requires boxing of the comparand (since the parameter is of type `Object`) but not the object doing the comparison. – supercat Aug 20 '12 at 14:46
10

From ECMA 335: Value Types will be sealed to avoid dealing with the complications of value slicing. The more restrictive rules specified here allow for more efficient implementation without severely compromising functionality.

I don't know what 'value slicing' means, but I would guess they are sealed to allow for efficient implementation of the CLR.

logicnp
  • 5,796
  • 1
  • 28
  • 32
1

You could have some limited form of inheritance using generic type parameters on value types.

leppie
  • 115,091
  • 17
  • 196
  • 297
-2

Because each instance of value-type hase different size and stored on the stack. So, if you write "Base = Derived" where "Base" and "Derived" are value-types you will corrupt stack.

user215303
  • 157
  • 1
  • 2