Pregunta

Por ejemplo:

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

Que genera este error desde el compilador:

  

Dependencia de clase base circular   involucrando 'A' y 'A.B'

Siempre pensé que una clase anidada se comportaba como una clase normal, excepto con reglas especiales sobre el acceso a los miembros privados de la clase externa, pero ¿supongo que hay alguna herencia implícita entre las dos clases?

¿Fue útil?

Solución

No hay herencia implícita involucrada por lo que puedo decir. Hubiera esperado que esto estuviera bien, aunque me imagino algo extraño si A y B fueran genéricos.

Se especifica en la sección 10.1.4 de la especificación:

  

Cuando una clase B deriva de una clase A,   es un error en tiempo de compilación para A   depende de B. Una clase depende directamente   en su clase base directa (si existe) y    depende directamente de la clase dentro   que se anida inmediatamente (si   ninguna). Dada esta definición, el   conjunto completo de clases sobre las cuales un   la clase depende es la transitiva   cierre de la directamente depende de   relación.

He resaltado la sección relevante.

Eso explica por qué el compilador lo rechaza, pero no por qué el lenguaje lo prohíbe. Me pregunto si hay una restricción de CLI ...

EDITAR: Bien, he recibido una respuesta de Eric Lippert. Básicamente, sería técnicamente posible (no hay nada en la CLI que lo prohíba), pero:

  • Permitir que sea difícil en el compilador, invalidar varios supuestos actuales en torno a pedidos y ciclos
  • Es una decisión de diseño bastante extraña que es más fácil de prohibir que apoyar

También se observó en el hilo del correo electrónico que haría válido este tipo de cosas:

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

... pero eso ya (como señaló Tinister) sería válido si B deriva de A.

Anidamiento + herencia = rareza ...

Otros consejos

Esto no es tanto C # como compilador. Uno de los trabajos de un compilador es diseñar una clase en la memoria, es decir, un conjunto de tipos de datos básicos, punteros, punteros de función y otras clases.

No puede construir el diseño para la clase A hasta que sepa cuál es el diseño de la clase B. No puede saber cuál es el diseño de la clase B hasta que terminó con el diseño de la clase A. Dependencia circular.

Creo que la anidación está destinada a representar que el tipo anidado es parte de la definición del tipo anidado. Con esa interpretación, la limitación tiene sentido porque cuando el compilador alcanza la definición de A, A.B aún no está definido, e incluso al final de A, ya está definido en términos de A.B.

Con respecto a las preguntas sobre lo que estaba intentando hacer:

Básicamente, quería crear una clase que tuviera una relación de composición consigo misma, pero no quería que el objeto contenido contuviera otros objetos y, por lo tanto, crear una cadena con muchos " A has-a A has- a A has-a A has-a ... '' relaciones Entonces mi pensamiento en ese momento era hacer algo como esto:

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

    private AA _containedObject;
}

Que en ese momento parecía bastante resbaladizo, pero en retrospectiva, no estoy tan seguro ...

Rebusqué en Google y no encontré ninguna buena discusión al respecto, así que pensé en publicarlo aquí.

Sin embargo, dentro de los comentarios de un publicación en el blog de Eric Lippert da ejemplos de una clase que implementa una interfaz anidada, así como una clase que implementa una interfaz genérica con una clase anidada como argumento de tipo (que no compila y él llama a un "error" en el compilador actual). Ambos ejemplos se refieren a interfaces, así que me preguntaba si había algunas reglas especiales con clases anidadas. Y parece que hay.

Pude evitar esto (al menos con interfaces) heredando de una clase separada que contenía las interfaces anidadas. (En mi caso, también estoy devolviendo referencias a estas interfaces).

En lugar 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

Haz algo como esto:

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 la fealdad con implementaciones de interfaz explícitas, también puede heredar de la otra clase, aunque eso no funcionaría si intentara heredar de una clase anidada, ya que no puede heredar de ambas clases.

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

Esto no tiene sentido para mí ... ¡Estás intentando extender algo que no existe! La clase B solo existe en el ámbito de la clase A y por eso creo que hay algún tipo de herencia.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top