32

For example:

public class A : A.B
{
    public class B { }
}

Which generates this error from the compiler:

Circular base class dependency involving 'A' and 'A.B'

I always figured a nested class behaved just like a regular class except with special rules concerning accessing the outer class's private members, but I guess there's some implicit inheritance occurring between the two classes?

Tinister
  • 11,097
  • 6
  • 35
  • 36
  • 3
    I wonder, is there any particular reason that you would want to do this, or did you post it for the sake of discussion and learning? What would be the practical application if it were possible? – Daan Nov 05 '08 at 16:11
  • @Daan while implementing a generic builder pattern fluent interface, I have interfaces in a generic class that I want to implement on that same class. because of this issue, I have to move the interfaces to a separate class (have to be in a class so that they can share the generic types and constraints). this has made the explicit interface implementation extremely ugly since I have to refer to that other class. (Actually I avoided the error and the ugliness by inheriting from that other class...) – Dave Cousineau Aug 29 '16 at 02:10

6 Answers6

34

There's no implicit inheritance involved as far as I can tell. I would have expected this to be okay - although I can imagine weirdness if A and B were generic.

It's specified in section 10.1.4 of the spec:

When a class B derives from a class A, it is a compile-time error for A to depend on B. A class directly depends on its direct base class (if any) and directly depends on the class within which it is immediately nested (if any). Given this definition, the complete set of classes upon which a class depends is the transitive closure of the directly depends on relationship.

I've highlighted the relevant section.

That explains why the compiler is rejecting it, but not why the language prohibits it. I wonder if there's a CLI restriction...

EDIT: Okay, I've had a response from Eric Lippert. Basically, it would be technically possible (there's nothing in the CLI to prohibit it), but:

  • Allowing it would be difficult in the compiler, invalidating various current assumptions around ordering and cycles
  • It's a pretty odd design decision which is easier to prohibit than to support

It was also noted on the email thread that it would make this kind of thing valid:

A.B x = new A.B.B.B.B.B.B.B.B.B.B.B.B();

... but that would already (as noted by Tinister) be valid if B derived from A.

Nesting + inheritance = oddness...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • I'm confused on how it would make that valid. A extends B and B doesn't have an nested class A, or something... – Tinister Nov 05 '08 at 22:22
  • Moreover, if B extended A, this type of thing is already possible: A.B.B.B.B.B.B.B.B.B.B.B.B.Foo(); – Tinister Nov 05 '08 at 22:24
  • Tinister: You're right, I got the example wrong :) Will edit. – Jon Skeet Nov 05 '08 at 22:36
  • (And yes, you're right, it is already possible. And weird. And to be avoided. I'd love to try to sneak it past a code review though...) – Jon Skeet Nov 05 '08 at 22:38
  • I suspect the biggest issue may have to do with scoping, since .net forbids a derived class from expanding the scope of its parent (a rule I don't entirely like anyway--an assembly which exports public classes X, Y, and Z, all derived from public class B, might benefit internally from having X and Y both derive from a class BB which derives from BB, but want to retain the freedom to change that inheritance relationship without affecting outside code). Some scoping scenarios can get weird if outer classes derive from inner ones. – supercat Jan 31 '13 at 21:47
13

This is not a C# thing as much as it is a compiler thing. One of the jobs of a compiler is to lay out a class in memory, that is a bunch of basic data types, pointers, function pointers and other classes.

It can't construct the layout for class A until it knows what the layout of class B is. It can't know what the layout of class B is until it finished with the layout of class A. Circular dependency.

Whaledawg
  • 4,234
  • 4
  • 26
  • 21
  • 4
    I'm not sure what you mean by this. Why couldn't it do the nested class first? That doesn't actually have any dependencies on the outer class in terms of layout, does it? – Jon Skeet Nov 05 '08 at 16:22
1

Regarding questions about what I was attempting to do:

Basically, I wanted to create a class that had a composition relationship with itself, but I didn't want to have the contained object to contain other objects and therefore create a chain with many "A has-a A has-a A has-a A has-a..." relationships. So my thought at the time was do something like this:

public class A : A.AA
{
    public class AA
    {
        // All of the class's logic
    }

    private AA _containedObject;
}

Which at the time seemed pretty slick but in retrospect I'm not so sure...

I had rummaged through Google and didn't find any good discussion on it so I thought I'd post it here.

However, within the comments of a post at Eric Lippert's Blog he gives examples of a class implementing a nested interface as well as a class implementing a generic interface with a nested class as the type argument (which doesn't compile and he calls a "bug" in the current compiler). Both of those examples concern interfaces so I was wondering if there was some special rules with nested classes. And it seems there are.

Tinister
  • 11,097
  • 6
  • 35
  • 36
  • There are certainly special rules - as per the spec. But it's fascinating to ask *why* those rules exist :) – Jon Skeet Nov 05 '08 at 17:17
0

I think the nesting is meant to represent that the nested type is part of the definition of the nesting type. With that interpretation, the limitation makes sense because at the time the compiler hits the definition of A, A.B is not yet defined, and even at the end of A, it is already defined in terms of A.B.

Jeff Kotula
  • 2,114
  • 12
  • 16
  • 2
    Why is this different from type A having a field of type B, and type B having a field of type A? The compiler already has to cope with that sort of situation... I suspect it may be for reasons of human sanity rather than actual technical reasons. – Jon Skeet Nov 05 '08 at 17:15
0

I was able to avoid this (at least with interfaces) by inheriting from a separate class containing the nested interfaces. (In my scenario I am also returning references to these interfaces.)

Instead of:

public class MyClass<T1, T2, T3> :
   MyClass<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }

   Interface Interface.SomeMethod() {
      ...
   }
}

// compile error: Circular base class dependency

Do something like this:

public sealed class MyClassInterfaces<T1, T2, T3>
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }
}

sealed class MyClass<T1, T2, T3> :
   MyClassInterfaces<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   MyClassInterfaces<T1, T2, T3>.Interface
   MyClassInterfaces<T1, T2, T3>.Interface.SomeMethod() {
      ...
   }
}

To avoid the ugliness with explicit interface implementations, you can also inherit from the other class, though that wouldn't work if you were trying to inherit from a nested class, since you can't inherit from both classes.

public abstract class MyClassInterfaces<T1, T2, T3>
where T1 : ...
where T2 : ... 
where T3 : ... {
   public interface Interface { Interface SomeMethod(); }
}

sealed class MyClass<T1, T2, T3> :
   MyClassInterfaces<T1, T2, T3>,
   MyClassInterfaces<T1, T2, T3>.Interface
where T1 : ...
where T2 : ... 
where T3 : ... {
   Interface Interface.SomeMethod() {
      ...
   }
}
Dave Cousineau
  • 12,154
  • 8
  • 64
  • 80
-2

This makes no sense to me... You are trying to extend something that doesn't exist !!! Class B only exists in the scope of class A and because of this I think there is some kind of inheritance.

bruno conde
  • 47,767
  • 15
  • 98
  • 117