clases - c# nested class
¿Por qué necesitaría usar clases anidadas de C# (8)
Lo que no entiendo es por qué necesitaría hacer esto
Creo que nunca necesitas hacer esto. Dada una clase anidada como esta ...
class A
{
//B is used to help implement A
class B
{
...etc...
}
...etc...
}
... siempre puedes mover la clase interna / anidada al ámbito global, así ...
class A
{
...etc...
}
//B is used to help implement A
class B
{
...etc...
}
Sin embargo, cuando B solo se usa para ayudar a implementar A, hacer que B sea una clase interna / anidada tiene dos ventajas:
- No contamina el alcance global (por ejemplo, el código del cliente que puede ver A, no sabe que la clase B existe)
- Los métodos de B tienen acceso implícito a miembros privados de A; mientras que si B no estuviera anidado dentro de A, B no podría acceder a los miembros de A a menos que esos miembros fueran internos o públicos; pero luego hacer que esos miembros sean internos o públicos también los expondría a otras clases (no solo a B); así que en lugar de eso, mantenga esos métodos de A privados y deje que B acceda a ellos declarando B como una clase anidada. Si conoces C ++, es como decir que en C # todas las clases anidadas son automáticamente un '' amigo '' de la clase en la que están contenidas (y que declarar una clase como anidada es la única forma de declarar amistad en C #, ya que C # no tiene una palabra clave de
friend
).
Cuando digo que B puede acceder a miembros privados de A, eso es asumiendo que B tiene una referencia a A; lo que hace a menudo, ya que las clases anidadas a menudo se declaran así ...
class A
{
//used to help implement A
class B
{
A m_a;
internal B(A a) { m_a = a; }
...methods of B can access private members of the m_a instance...
}
...etc...
}
... y construido a partir de un método de A usando un código como este ...
//create an instance of B, whose implementation can access members of self
B b = new B(this);
Puedes ver un ejemplo en la respuesta de Mehrdad.
Esta pregunta ya tiene una respuesta aquí:
Estoy tratando de entender acerca de las clases anidadas en C #. Entiendo que una clase anidada es una clase que se define dentro de otra clase, lo que no entiendo es por qué necesitaría hacer esto.
¿Quizás este es un buen ejemplo de cuándo usar clases anidadas?
// ORIGINAL
class ImageCacheSettings { }
class ImageCacheEntry { }
class ImageCache
{
ImageCacheSettings mSettings;
List<ImageCacheEntry> mEntries;
}
Y:
// REFACTORED
class ImageCache
{
Settings mSettings;
List<Entry> mEntries;
class Settings {}
class Entry {}
}
PD: no he tenido en cuenta qué modificadores de acceso deben aplicarse (privado, protegido, público, interno)
El propósito es típicamente restringir el alcance de la clase anidada. Las clases anidadas en comparación con las clases normales tienen la posibilidad adicional del modificador private
(y, por supuesto, también están protected
).
Básicamente, si solo necesita usar esta clase dentro de la clase "principal" (en términos de alcance), entonces es apropiado definirla como una clase anidada. Si esta clase necesita ser usada sin el ensamblaje / biblioteca , entonces es usualmente más conveniente para el usuario definirla como una clase separada (hermana), ya sea que exista o no una relación conceptual entre las dos clases. A pesar de que es técnicamente posible crear una clase public
anidada dentro de una clase padre public
, en mi opinión esto rara vez es algo apropiado para implementar.
Hay ocasiones en que es útil implementar una interfaz que se devolverá dentro de la clase, pero la implementación de esa interfaz debe estar completamente oculta al mundo exterior.
Como ejemplo, antes de agregar el rendimiento a C #, una forma de implementar enumeradores era poner la implementación del enumerador como una clase privada dentro de una colección. Esto brindaría un fácil acceso a los miembros de la colección, pero el mundo exterior no necesitaría / ver los detalles de cómo se implementa esto.
Las clases anidadas son muy útiles para implementar detalles internos que no deben ser expuestos. Si usa Reflector para consultar clases como Dictionary <Tkey, TValue> o Hashtable, encontrará algunos ejemplos.
También hay buenos usos de los miembros públicos anidados ...
Las clases anidadas tienen acceso a los miembros privados de la clase externa. Por lo tanto, un escenario en el que esta es la forma correcta sería al crear un Comparer (es decir, implementar la interfaz de IComparer).
En este ejemplo, FirstNameComparer tiene acceso al miembro privado _firstName, que no lo haría si la clase fuera una clase separada ...
public class Person
{
private string _firstName;
private string _lastName;
private DateTime _birthday;
//...
public class FirstNameComparer : IComparer<Person>
{
public int Compare(Person x, Person y)
{
return x._firstName.CompareTo(y._lastName);
}
}
}
Un patrón que me gusta particularmente es combinar clases anidadas con el patrón de fábrica:
public abstract class BankAccount
{
private BankAccount() {} // prevent third-party subclassing.
private sealed class SavingsAccount : BankAccount { ... }
private sealed class ChequingAccount : BankAccount { ... }
public static BankAccount MakeSavingAccount() { ... }
public static BankAccount MakeChequingAccount() { ... }
}
Al anidar las clases como esta, hago imposible que terceros creen sus propias subclases. Tengo control completo sobre todo el código que se ejecuta en cualquier objeto de cuenta bancaria. Y todas mis subclases pueden compartir detalles de implementación a través de la clase base.
Una clase anidada puede tener modificadores de acceso protected internal
private
, protected
y protected internal
junto con public
e internal
.
Por ejemplo, está implementando el método GetEnumerator()
que devuelve un objeto IEnumerator<T>
. Los consumidores no se preocuparían por el tipo real del objeto. Todo lo que saben al respecto es que implementa esa interfaz. La clase que desea devolver no tiene ningún uso directo. Puede declarar esa clase como una clase anidada private
y devolver una instancia de ella (así es como el compilador de C # implementa los iteradores):
class MyUselessList : IEnumerable<int> {
// ...
private List<int> internalList;
private class UselessListEnumerator : IEnumerator<int> {
private MyUselessList obj;
public UselessListEnumerator(MyUselessList o) {
obj = o;
}
private int currentIndex = -1;
public int Current {
get { return obj.internalList[currentIndex]; }
}
public bool MoveNext() {
return ++currentIndex < obj.internalList.Count;
}
}
public IEnumerator<int> GetEnumerator() {
return new UselessListEnumerator(this);
}
}