estructura - ¿Por qué una clase no puede extender su propia clase anidada en C#?
clases y metodos en c# (6)
Creo que la anidación pretende representar que el tipo anidado es parte de la definición del tipo de anidación. Con esa interpretación, la limitación tiene sentido porque en el momento en que el compilador alcanza la definición de A, AB aún no está definido, e incluso al final de A, ya está definido en términos de AB.
Por ejemplo:
public class A : A.B
{
public class B { }
}
Lo que genera este error del compilador:
Dependencia de clase de base circular que implica ''A'' y ''A.B''
Siempre pensé que una clase anidada se comportó como una clase normal, excepto con reglas especiales sobre el acceso a los miembros privados de la clase externa, pero supongo que hay una herencia implícita entre las dos clases.
En cuanto 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 tener el objeto contenido para contener otros objetos y, por lo tanto, crear una cadena con muchos "A has-a A has-a A has-" una A tiene ... "relaciones. Entonces mi pensamiento en ese momento fue 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 una 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 llama " error "en el compilador actual). Ambos ejemplos se refieren a interfaces, por lo que me preguntaba si había algunas reglas especiales con clases anidadas. Y parece que hay
Esto no es tanto una cosa de C # como algo de compilación. 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 a funciones 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 finaliza con el diseño de la clase A. Dependencia circular.
Esto no tiene sentido para mí ... ¡Estás tratando de extender algo que no existe! La clase B solo existe en el ámbito de la clase A y, debido a esto, creo que hay algún tipo de herencia.
No hay herencia implícita involucrada por lo que yo sé. Hubiera esperado que todo estuviera bien, aunque puedo imaginar rareza si A y B son 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 depender de B. Una clase depende directamente de su clase base directa (si existe) y depende directamente de la clase dentro de la cual se anida inmediatamente ( Si alguna). Dada esta definición, el conjunto completo de clases del que depende una clase es el cierre transitivo de la relación directamente dependiente.
He resaltado la sección relevante.
Eso explica por qué el compilador lo está rechazando, pero no por qué el lenguaje lo prohíbe. Me pregunto si hay una restricción CLI ...
EDITAR: Bien, he recibido una respuesta de Eric Lippert. Básicamente, sería técnicamente posible (no hay nada en la CLI para prohibirlo), pero:
- Permitirlo sería difícil en el compilador, invalidando varias suposiciones actuales sobre el orden y los ciclos
- Es una decisión de diseño bastante extraña que es más fácil de prohibir que de apoyo
También se observó en el hilo del correo electrónico que haría que este tipo de cosas fueran válidas:
A.B x = new A.B.B.B.B.B.B.B.B.B.B.B.B();
... pero eso ya sería (como señaló Tinister) válido si B derivó de A.
Nesting + inheritance = rareza ...
Pude evitar esto (al menos con interfaces) al heredar de una clase separada que contiene 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 estuviera tratando de 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() {
...
}
}