subdeveloper
subdeveloper

Reputation: 2668

Nested class - isn't this an incomplete type

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:

  1. 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?)

  2. Is this runtime exception intentional, or is it something that should be a compile time error?

Upvotes: 1

Views: 185

Answers (1)

Jon Skeet
Jon Skeet

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

Related Questions