Por que uma classe não pode estender a sua própria classe aninhada em C #?

StackOverflow https://stackoverflow.com/questions/265585

  •  06-07-2019
  •  | 
  •  

Pergunta

Por exemplo:

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

O que gera este erro do compilador:

base circular dependência classe envolvendo 'A' e 'A.B.'

Sempre achei uma classe aninhada se comportou como uma classe regular, exceto com regras especiais em matéria de acesso aos membros privados da classe externa, mas eu acho que há alguma herança implícita ocorrendo entre as duas classes?

Foi útil?

Solução

Não há nenhuma herança implícita envolvidos, tanto quanto eu posso dizer. Eu teria esperado que este seja bem -. Embora eu possa imaginar estranheza se A e B foram genérico

É especificado no ponto 10.1.4 da especificação:

Quando uma classe B deriva de uma classe A, é um erro de tempo de compilação para A dependem B. Uma classe depende directamente na sua classe base direta (se houver) e depende diretamente da classe dentro que é imediatamente aninhados (se qualquer). Dada esta definição, o conjunto completo de aulas sobre a qual uma classe depende é o transitivo fecho do directamente depende relacionamento.

Eu destacou a seção relevante.

Isso explica por que o compilador é rejeitá-la, mas não por que a linguagem proíbe. Gostaria de saber se há uma restrição CLI ...

EDIT: Ok, eu tive uma resposta de Eric Lippert. Basicamente, seria tecnicamente possível (não há nada na CLI para proibi-la), mas:

  • Permitir que seria difícil no compilador, invalidando várias suposições atuais cerca de pedidos e ciclos
  • É uma decisão de projeto muito estranho que é mais fácil para proibir que para suporte

Ele também foi observado no segmento de e-mail que faria este tipo de coisa válido:

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

... mas isso já (como observado por Tinister) ser válido se B derivada de A.

Assentamento + herança = estranheza ...

Outras dicas

Esta não é uma coisa # C tanto como ele é uma coisa do compilador. Um dos trabalhos de um compilador é colocar para fora uma classe na memória, que é um monte de tipos básicos de dados, ponteiros, ponteiros de função e outras classes.

Não se pode construir o layout de classe A até que se sabe o que o layout de classe B é. Ele não pode saber o que o layout de classe B é até que ele terminou com o layout de classe A. dependência circular.

Eu acho que o assentamento é utilizado para representar que o tipo aninhado é parte da definição do tipo de nidificação. Com essa interpretação, a limitação faz sentido, porque no momento em que o compilador atinge a definição de A, A.B. ainda não está definida, e até mesmo no final de A, ele já está definido em termos de A.B..

No que diz respeito perguntas sobre o que eu estava tentando fazer:

Basicamente, eu queria criar uma classe que tinha um relacionamento de composição com si mesmo, mas eu não queria ter o objeto contido para conter outros objetos e, portanto, criar uma cadeia com muitos "Um tem-um Um tem-um a tem-a a tem-a ..." relacionamentos. Então, meu pensamento na época era fazer algo como isto:

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

    private AA _containedObject;
}

que na época parecia muito escorregadio, mas em retrospecto, eu não tenho tanta certeza ...

Eu tinha vasculhou Google e não havia nenhuma boa discussão sobre isso, então eu pensei que eu ia postar aqui.

No entanto, dentro dos comentários de um posto no Blog do Eric Lippert ele dá exemplos de uma classe que implementa uma interface aninhada, bem como uma classe que implementa uma interface genérica, com uma classe aninhada como o argumento de tipo (que não compila e ele chama de um "bug" no compilador atual). Ambos os exemplos dizem respeito aos interfaces de então eu queria saber se havia algumas regras especiais com classes aninhadas. E parece que há.

I foi capaz de evitar este (pelo menos com interfaces) por herança a partir de uma classe separada contendo as interfaces aninhadas. (Em meu cenário Eu também estou retornando referências a essas interfaces.)

Em vez de:

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

fazer algo como isto:

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

Para evitar a feiúra com implementações de interfaces explícitas, você também pode herdar de outra classe, embora isso não iria funcionar se você estivesse tentando herdar de uma classe aninhada, desde que você não pode herdar de ambas as 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() {
      ...
   }
}

Isso não faz sentido para mim ... Você está tentando estender algo que não existe !!! Classe B só existe no âmbito da classe A e por isso eu acho que há algum tipo de herança.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top