为什么C#中一个类不能继承它自己的嵌套类?

32
例如:
public class A : A.B
{
    public class B { }
}

这会导致编译器生成以下错误:

涉及“ A”和“ A.B”的循环基类依赖关系

我一直认为嵌套类的行为就像常规类一样,只是有关于访问外部类私有成员的特殊规则,但我猜在这两个类之间发生了一些隐式继承?


3
我想知道,你为什么想要这样做,是有特别的原因吗?还是只是为了讨论和学习而发帖?如果可能的话,它的实际应用会是什么? - Daan
@Daan 在实现通用构建器模式流畅接口时,我在一个通用类中有一些接口需要在同一类中实现。由于这个问题,我不得不将这些接口移动到一个单独的类中(必须在一个类中,以便它们可以共享通用类型和约束)。这使得显式接口实现变得非常丑陋,因为我不得不引用那个其他的类。(实际上,我通过继承那个其他的类来避免了错误和丑陋...) - Dave Cousineau
6个回答

34
据我所知,这里没有隐式继承。虽然我认为这应该是可以的——但如果A和B是泛型,则可能存在奇怪的情况。
规范的第10.1.4节中指定了这一点:
当类B从类A派生时,A依赖于B是编译时错误。一个类直接依赖于其直接基类(如果有的话),并直接依赖于它被立即嵌套在其中的类(如果有的话)。根据这个定义,一个类依赖的完整集合是直接依赖于关系的传递闭包。
我已经突出了相关部分。
这解释了为什么编译器会拒绝它,但不解释为什么语言禁止它。我想知道是否存在CLI限制...
编辑:好吧,我收到了Eric Lippert的回复。基本上,这是在技术上是可能的(CLI中没有任何东西来禁止它),但是:
- 在编译器中允许它将很困难,会使当前关于排序和循环的各种假设无效。 - 这是一个相当奇怪的设计决定,比禁止更容易支持。
邮件线程还指出,它会使这种情况有效:
A.B x = new A.B.B.B.B.B.B.B.B.B.B.B.B();

... 但是如果B源自A,那么这已经是有效的(如Tinister所指出的)。

嵌套+继承=奇怪的事情...


我对它如何使其有效感到困惑。A继承B,而B没有嵌套类A,或者什么的... - Tinister
此外,如果B扩展了A,那么这种类型的事情已经是可能的:A.B.B.B.B.B.B.B.B.B.B.B.Foo(); - Tinister
Tinister: 你说得对,我的例子错了 :) 我会进行编辑。 - Jon Skeet
(是的,你说得对,这已经是可能的了。而且很奇怪,应该避免使用。不过我很想试图在代码审查中通过它......) - Jon Skeet
我怀疑最大的问题可能与作用域有关,因为.NET禁止派生类扩展其父类的作用域(我完全不喜欢的一条规则 - 导出公共类X、Y和Z的程序集都派生自公共类B,内部可能会受益于将X和Y都派生自从BB派生的类BB,但希望保留更改继承关系的自由而不影响外部代码)。如果外部类从内部类派生,则某些作用域方案可能会变得奇怪。 - supercat

13

这不仅仅是C#的问题,更是编译器的问题。编译器的工作之一就是将类在内存中进行布局,包括一些基本数据类型、指针、函数指针和其他类。

它无法构建类A的布局,直到它知道类B的布局是什么。它不能知道类B的布局是什么,直到它完成了类A的布局。循环依赖。


4
我不确定您的意思。为什么不能先处理嵌套类?就排版而言,这并没有依赖于外部类,对吗? - Jon Skeet

1
关于我所尝试做的事情的问题:
基本上,我想创建一个与自身具有组合关系的类,但我不想让包含的对象包含其他对象,从而创建一个带有许多“A has-a A has-a A has-a A has-a ...”关系的链。因此,当时我的想法是这样做:
public class A : A.AA
{
    public class AA
    {
        // All of the class's logic
    }

    private AA _containedObject;
}

当时看起来很不错,但回想起来我并不确定...

我在谷歌上搜了一下,没有找到任何好的讨论,所以我想在这里发帖。

然而,在Eric Lippert博客上的一篇文章的评论中,他给出了一个实现嵌套接口的类的示例,以及一个实现具有嵌套类作为类型参数的泛型接口的类的示例(它无法编译,他称之为当前编译器中的“错误”)。 这两个示例都涉及接口,因此我想知道是否有一些关于嵌套类的特殊规则。 看来确实有。


有一些特殊规则,根据规范来说是这样的。但是问一下为什么存在这些规则,真的很有趣 :) - Jon Skeet

0

我能够避免这个问题(至少在接口方面)通过继承一个包含嵌套接口的单独类。(在我的情况下,我也返回对这些接口的引用。)

与其:

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

做类似这样的事情:

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() {
      ...
   }
}

为了避免显式接口实现带来的丑陋,你也可以从另一个类继承,但如果你试图从嵌套类继承,那么这种方法就行不通了,因为你不能同时从两个类继承。
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() {
      ...
   }
}

0

我认为嵌套的含义是表示嵌套类型是嵌套类型定义的一部分。以这种解释方式,限制是有意义的,因为当编译器访问 A 的定义时,A.B 尚未定义,即使在 A 结束时,它也已经在 A.B 的基础上定义。


2
为什么这与类型A具有类型B的字段和类型B具有类型A的字段不同?编译器已经必须应对这种情况...我怀疑这可能是出于人类理智而不是实际技术原因。 - Jon Skeet

-2

这对我来说毫无意义...你试图扩展不存在的东西!!! B类仅存在于A类的范围内,因此我认为存在某种继承关系。


1
不,没有隐式继承。从A的外部引用A.B是完全可行的。 - Jon Skeet
1
但是依赖关系到底在哪里?如果允许上述代码,会出现什么确切的问题? - Jon Skeet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接