Reputation: 2668
Sample code:
public class SimpleClass
{
private NestedClass nestedClass = new NestedClass();
public class NestedClass : SimpleClass
{
}
}
// when instantiating in say.. main()
SimpleClass simpleClass = new SimpleClass();
I come from C++ background, and so I am having difficulty wrapping my head around what is going on here. Specifically how can we instantiate NestedClass
inside SimpleClass
. Since ideally compiler will need full layout of SimpleClass
to be able to instantiate NestedClass
, which in turn needs SimpleClass
. Its kinda recursive in nature.
And this is what happens when we run this code. We get a StackOverflow
:) exception!
In C++ world, compiler would cry "incomplete type
" in such cases.
So the crux of my question is:
What's going on here, how does compiler layout this class (I know its implementation detail, but without complete type how can we instantiate an object?)
Is this runtime exception intentional, or is it something that should be a compile time error?
Upvotes: 1
Views: 185
Reputation: 1500105
The fact that it's a nested class is irrelevant here. The fact that it's a derived class is for more important. Aside from details around generics and access (e.g. nested classes having access to private members of the containing class) there's relatively little difference between an nested class and a non-nested class.
So let's show that:
class BaseClass
{
DerivedClass derived = new DerivedClass();
}
class DerivedClass : BaseClass
{
}
This still compiles, and still fails with a StackOverflowException
if you try to create an instance of BaseClass
(or DerivedClass
).
The layout is fine: BaseClass
has a single field, which is a reference to a DerivedClass
. There's no issue in terms of knowing "how big" either BaseClass
or DerivedClass
are - and indeed without the initialization aspect, this would be absolutely fine. What's important here is that DerivedClass
is a reference type, so the value of the derived
field is just a reference.
There are no C# language rules being broken here, and designing a language rule that would prohibit this without prohibiting valid use cases would be really hard.
The recursion here really isn't that different to what you can do with a single class:
class BaseClass
{
BaseClass other = new BaseClass();
}
Again, it's perfectly valid code, but causes a stack overflow.
The problem I suspect you're thinking of is easily demonstrated with structs, although without inheritance involved:
struct Tweedledum
{
Tweedledee dee;
}
struct Tweedledee
{
Tweedledum dum;
}
This does fail, because there's no appropriate layout: a Tweedledum
value contains a Tweedledee
value directly, and vice versa. Not a reference: the actual value.
The error here is:
error CS0523: Struct member 'Tweedledum.dee' of type 'Tweedledee' causes a cycle in the struct layout
error CS0523: Struct member 'Tweedledee.dum' of type 'Tweedledum' causes a cycle in the struct layout
Upvotes: 6