Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>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.</p> <p>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.</p> <p>(There’s an exception: a value type object can be boxed, which allows for virtual methods inherited from <code>System.Object</code> to be called.)</p> <p>To address one of your points:</p> <blockquote> <ul> <li>You could cast from a derived struct to the base, since they would overlap the same memory.</li> </ul> </blockquote> <p>No, this would not be possible – casting a value type would <em>copy</em> 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 <code>object</code> which actually performs boxing under the hood, and <em>also</em> operates on a <em>copy</em> of the value).</p> <p><strong>Still not clear?</strong> Let’s look at an example.</p> <p>Let’s say we’ve got the hypothetical <code>struct Shape</code> and, inheriting from it, the <code>struct Circle</code>. <code>Shape</code> defines a virtual <code>Draw</code> method (which accepts a <code>Graphics</code> object). Now, let’s say we want to draw a shape on a canvas. This, of course, works perfectly well:</p> <pre><code>var circle = new Circle(new Point(10, 10), 20); circle.Draw(e.Graphics); // e.Graphics = graphics object of our form. </code></pre> <p>– But here we don’t actually use inheritance at all. To make use of inheritance, imagine instead the following <code>DrawObject</code> helper method:</p> <pre><code>void DrawObject(Shape shape, Graphics g) { // Do some preparation on g. shape.Draw(g); } </code></pre> <p>And we call it elsewhere with a <code>Circle</code>:</p> <pre><code>var circle = new Circle(new Point(10, 10), 20); DrawObject(circle, e.Graphics); </code></pre> <p>– And, <strong>ka-blam</strong> – this code doesn’t draw a circle. Why? Because when we pass the circle to the <code>DrawObject</code> method, we do two things:</p> <ul> <li>We <em>copy</em> it.</li> <li>We <em>slice</em> it, i.e. the object <code>shape</code> object is really no longer a <code>Circle</code> – neither the original one nor a copy. Instead, its <code>Circle</code> portion was “sliced” away during copying and only the <code>Shape</code> portion remains. <code>shape.Draw</code> now calls the <code>Draw</code> method of <code>Shape</code>, not of <code>Circle</code>.</li> </ul> <p>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.</p> <p>Notice that the above code <em>does</em> work in .NET if <code>Shape</code> is an interface. In other words, a <em>reference</em> type. Now the situation is different: your <code>circle</code> object will <em>still</em> be copied but it will also be boxed into a reference.</p> <p>Now, .NET <em>could</em> theoretically allow you to inherit a <code>struct</code> from a <code>class</code>. Then the above code would work just as well as if <code>Shape</code> were an interface. But then, the whole advantage of having a <code>struct</code> in the first place vanishes: for all intents and purposes (except for local variables which <em>never</em> get passed to another method, hence no utility of inheritance) your <code>struct</code> would behave as an immutable reference type instead of a value type.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload