Why C# structs cannot be inherited? [duplicate]
Asked Answered
E

3

125

I am reading CLR via C# by Jeffery Richter and it says a struct is a value type and cannot be inherited.

Are there any technical or philosophical reasons?

ADD 1 - 5:53 PM 11/11/2020

Every decision, no matter how reasonable or accidental it may look, has its impact, subtle or profound. We try to decouple things, sometimes by creating new coupling...

Ecbatana answered 22/2, 2010 at 10:7 Comment(4)
Maybe more a question than an answer, but all structs inherit System.ValueType and if you look with Reflector, System.ValueType is an abstract class :) All structs inherit it. I think it would be beneficial if the responses to this question clarified this.Adlay
@Marek: Read the second paragraph of my answer here for clarification on that: #1979089 To sum up, inheriting from ValueType doesn't have a structural implication for the struct instance. In fact, a struct value is not really a System.ValueType as far as runtime is concerned (i.e. in IL, you can't pass an int to a method that takes a ValueType without explicitly using the box instruction). Only a boxed struct (which is a reference type) really is.Aitken
@Ecbatana - I'm not sure what value your latest edit adds to the question.Bait
@WaiHaLee Just some extra thought after revisiting this question.Ecbatana
I
119

Edit: There are serious editorial concerns about this post, apparently. See comment section.

A little of both.

Philosophically, it works out - there are classes, which are the "real" building block for object oriented programming, and there are structs, which are lightweight data types for storage but allow object-like method calls for familiarity and convenience.

Technically, being a "value type" means that the entire struct - all of its contents - are (usually) stored wherever you have a variable or member of that type. As a local variable or function parameter, that means on the stack. For member variables, that means stored entirely as part of the object.

As a (primary) example of why inheritance is a problem, consider how storage is affected at a low level if you allowed structs to have subtypes with more members. Anything storing that struct type would take up a variable amount of memory based on which subtype it ended up containing, which would be an allocation nightmare. An object of a given class would no longer have a constant, known size at compile time and the same would be true for stack frames of any method call. This does not happen for objects, which have storage allocated on the heap and instead have constant-sized references to that storage on the stack or inside other objects.

This is just an intuitive, high-level explanation - See comments and other answers for both expanded and more precise information.


Edit: The link in the comments to Eric Lippert's article The Stack Is An Implementation Detail, is now located on his personal blog site.

Indomitability answered 22/2, 2010 at 10:8 Comment(12)
Thanks, Jesse. Your explanation gave me some sparks. As to my understanding, OOP comes at a cost. If there's only "ref-type" and everything happens on the heap, whose management is far less efficient than the stack, the performance will be poor. So there comes the so-called "value-type" which lives on the stack for better performance, and it is because of the place of the allocation and further the memory usage paradigm that make the value-type sealed.Ecbatana
...and if we can figure out a brand-new paradigm of using memory besides stack and heap, maybe new data types will arise.Ecbatana
As to brand-new paradigms of using memory... It's doubtful. :) Any other paradigm of using memory is likely to be layered on top of stack or heap allocation or both. (Such as closures in functional languages, which might be worth your time to understand.)Indomitability
@Ecbatana you should refer to Eric Lippert's article The Stack is An Implementation DetailGlum
I could see some substantial benefits to allowing the definition of a "derived struct" which would support bidirectional identity preserving conversion to/from the type from which it was derived. The "derived struct type" would not be allowed to define any new fields, and would have some limits as to what it could do, but could still be useful in some contexts. See https://mcmap.net/q/182055/-the-dream-to-inherit-from-a-struct-in-c if you like.Rosalinarosalind
@JesseMillikan: The paradigm I would like to see would be to define properties such that one could write CollectionOfPoints[subscript].X += Foo and have the compiler translate that to something like CollectionOfPoints.ActOnPoint(4, (ref Point it, ref int p1) => {it.X += p1;}, ref Foo) (for that to work in all cases, there would have to be a means of accepting a variadic pass-through delegate). Such a feature would give the collection a chance to act upon the changed value of it--an ability which even languages that can return references can't really offer.Rosalinarosalind
There is a gap in that philosophy: there are also custom attributes that can be applied to a struct but there is no way add more of them afterwards: cannot mark a third-party API struct as [Serializable].Fillip
Note that C# structs are conceptually the same as C++ objects, and in C++ inheritance is possible. However, the problem you mentioned is solved through object slicing, which is very unintuitive (and is basically an implementation-detail leaking into language)Synthetic
Why does this answer have so many up votes and why was it accepted as an answer. It very briefly mentions issues, but does not actually answer the question 'Why a C# struct cannot be inherited?' There are two ways this can be answered - 1) What the current spec says about struct inheritance - can it be done or can it not be done. Although the question's body actually answers it's own question 8-/ and 2) Is it technically possible and feasible to amend the spec in order to support struct inheritance. If/when we get struct inheritance in C#, the answers here will appear lacking.Interlocutor
Just because it's stored on the stack is no excuse -- The CLR has a managed stack, and the CLR knows exact type of your struct at runtime; there's no reason it shouldn't be able to allocate the exact amount of space accordingly.Quarrelsome
I didn't say it couldn't be done. As for why this response was accepted, it's because the asker chose it at the time. I could write a better answer at some point in the future; unfortunately SE doesn't really incentivize that.Indomitability
@BrainSlugs83: This is ok if you don't mutate the variables. But what would happen is this situation: ParentS x = GetParentOrChildRandomly(), where ParentS occupies 24B and ChildS : ParentS occupies 32B? The runtime can't know the stack size beforehand. Also, imagine this but happening with a field of an object in the heap. The GC would have to find a way to add (or remove) space from an existing object in the heap (that is possibly already compacted).Filide
S
51

Because it is the way structs are represented in .NET. They are value types and value types don't have a method table pointer allowing inheritance.

Sotted answered 22/2, 2010 at 10:11 Comment(6)
One might add: And the reason why they don't have a method table pointer is because value types are designed to be as lightweight as possible.Marna
Except that structs can implement interfaces, and a method table pointer is used for calling them ...Metasomatism
There are a number of ways in which structs could usefully support inheritance without needing per-instance type information: (1) by saying that the only effect of FooStruct:BarStruct would be that a generic type constrained to BarStruct would be able to access members (including fields) of that type directly; (2) by saying that any type FooStruct:BarStruct must be contractually bound to use its inherited fields in such a fashion that a BarStruct formed by copying the inherited fields of an existing FooStruct must be a valid BarStruct, and...Rosalinarosalind
...taking a valid FooStruct and BarStruct, and modifying a the former by copying all the fields from the latter to the former, would yield a FooStruct that was still valid. Structures which would be broken if "sliced" should be sealed, but some structures' behavior would remain perfectly sensible even when sliced (especially those which have no fields other than the ones they inherit).Rosalinarosalind
Aren't method vtables (as they exist in C++) only necessary to allow a class and its derived class to have separate methods with the same name? This makes me suspect of @Bevan's commentSegregate
I don't know about C++ - but method tables are absolutely used for dispatch of method calls via interface references in the CLR. Check out this question for more: #9809482Metasomatism
G
24

You may find the answers to SO Question Why are .NET value types sealed? relevant. In it, @logicnp refers to ECMA 335, which states:

8.9.10 Value type inheritance

  • [...]
  • 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.
Glum answered 21/2, 2011 at 4:54 Comment(2)
Where by "without severely compromising functionality", they clearly meant "by severely compromising functionality".Constantan
@GlennMaynard This is obviously a matter of opinion. I was just hoping I could do this but it's logical, given the way c# works, that it would be a bad idea. The compromise is minimal, and if you think that's too much, you're free to use C++ which is much better at this stuff with the given problem that coding in C++ is often more cumbersome.Hairball

© 2022 - 2024 — McMap. All rights reserved.