2009-03-26 16 views
45

Estoy trabajando en algún código heredado y he encontrado algo de lo que no estoy seguro. Tenemos un class y que se declara dentro de otro class x. Class y solo se usa dentro de class x, pero mi pregunta es ¿por qué no crearías un archivo de clase separado y pondrías class y en lugar de declararlo dentro de class x? ¿No es esto una violación de los OOP o es solo una cuestión de estilo ya que solo se usa dentro de esta clase? Estoy refaccionando parte de este código y mi primera reacción sería separar class y en su propio archivo.
Clase declarada dentro de otra clase en C#

namespace Library 
{ 
    public class x 
    { 
     // methods, properties, local members of class x 

     class y 
     { 
     // methods, properties, local members of class y 
     } 
    } 
} 

Respuesta

58

se crea una clase interna, ya que sólo se utiliza siempre dentro del ámbito de la clase X y que lógicamente cabe en la factorización/Arquitectura de la clase x.

La clase y también podría estar al tanto de los detalles de implementación de la clase x que no están destinados a ser conocidos por el público.

+0

¿Es esto verdad? De mi prueba, la clase Y no puede acceder a los detalles de la clase X. ¿Cómo sería eso posible? – Nitax

+2

No importa, acaba de encontrar http://blogs.msdn.com/b/oldnewthing/archive/2006/08/01/685248.aspx – Nitax

19

Esto tiene implicaciones de permisos. Una "clase y" de alto nivel sería "interna", sin embargo, aquí "y" es privado para "x". Este enfoque es útil para detalles de implementación (por ejemplo, filas de caché, etc.). Del mismo modo, y tiene acceso a todos los estados privados de x.

También hay implicaciones con los genéricos; x<T>.y es genérico "de T", heredado de la clase externa. Puede ver esto aquí, donde Bar tiene un uso completo de T - y tenga en cuenta que los campos estáticos de Bar tienen un alcance de T.

class Foo<T> { 
    void Test(T value) { 
     Bar bar = new Bar(); 
     bar.Value = value; 
    } 
    class Bar { 
     public T Value { get; set; } 
    } 
} 

A menudo las personas piensan erróneamente que necesitan para definir Bar como Bar<T> - esto es ahora (efectivamente) doblemente genérico - es decir, Foo<TOld, T> - donde TOld es el (ahora no disponible) T de Foo<T>. ¡Entonces no hagas eso! O si quiere que sea doblemente genérico, elija nombres diferentes. Afortunadamente, el compilador le advierte acerca de esto ...

+3

+1 Puede ser una buena idea explicar cómo "T" de la clase externa ser parte del tipo cerrado y construido de la clase interna. Es un punto importante que mucha gente desconoce. –

+0

¿Necesita reenviar declarar 'Barra' antes de usarlo en la definición de' Prueba() '? –

2

Creo que está bien, siempre que la clase contenida se utilice únicamente como utilidad. Utilizo este tipo de constructo, por ejemplo, para definir tipos de retorno complejos para métodos privados.

2

Acabo de revisar el código que estoy actualizando (y lo escribí originalmente) y eliminé todas las clases anidadas. Desafortunadamente, originalmente utilicé la clase anidada fuera de la clase en la que estaba definida. Mover clases anidadas hizo una gran diferencia para mí porque originalmente tenía un diseño incorrecto.

Si Y sólo se utiliza en X y nunca se utiliza fuera de X, yo diría que mantenerlo allí

7

Este código está muy bien para la razón exacta que usted ha dado - "clase Y es sólo alguna vez usado dentro de la clase x ". Esos son nested types y una de las pautas para usarlos es que los tipos anidados deben estar estrechamente relacionados con su tipo de declaración y no deben ser útiles como un tipo de propósito general. De esta manera, la clase anidada es inaccesible para otras clases, pero aún así le permite seguir principios orientados a objetos.

2

Déjeme darle un ejemplo del uso de clases anidadas que podrían aclarar cuándo este tipo de arquitectura es apropiada. Hace poco tuve que generar una tabla HTML tirando de columnas seleccionadas de una tabla de datos y "pivotándolas" para que las filas se convirtieran en columnas y viceversa.En mi caso, hubo dos operaciones esenciales: pivotar los datos y generar un resultado bastante complejo (no solo estaba mostrando los datos: cada fila de columna/tabla de datos estaba sujeta a operaciones para extraer el título, generar etiquetas de imagen, configurar enlaces, etc. por lo tanto, usar un Pivot de SQL tampoco era realmente correcto).

Después de un intento inicial de crear una clase para hacer todo, reconocí que gran parte de los datos/métodos caía en tres particiones distintas: procesamiento de encabezado, procesamiento de fila y pivoteo. Por lo tanto, decidí que un mejor enfoque sería encapsular la lógica para "encabezado" y "fila" en clases separadas, anidadas. Esto me permitió separar los datos contenidos en cada fila y programar las operaciones de pivote de forma muy limpia (llamando a un objeto de fila separado para cada columna en su tabla de datos). Al final de las operaciones de pivote, generé la salida llamando al objeto de encabezado y luego a cada objeto de fila para generar su salida de vuelta a la clase principal.

Las clases separadas no eran apropiadas porque A) las clases anidadas necesitaban algunos datos de la clase maestra y B) el procesamiento era muy específico y no era útil en ninguna otra parte. Solo programar una clase grande era simplemente más complicado debido a la confusión que rodeaba a los términos como "columna" y "fila", que diferían dependiendo de si hablabas de datos o de salida de HTML. Además, este fue un trabajo inusual en el sentido de que estaba generando HTML en mi clase de negocios, así que quería separar la lógica empresarial pura de la generación de IU. Al final, las clases anidadas proporcionaron el equilibrio perfecto, entonces, de la encapsulación y el intercambio de datos.

1

Aún podría refactorizar su clase y en otro archivo, pero use una clase parial. El beneficio de esto es que todavía tiene una clase por archivo y no tiene las molestias de refactorización de mover la declaración fuera de la clase x.

p. Ej. podría tener un archivo de código: x.y.cs que se parecería a

partial class X 
{ 
    class Y 
    { 
     //implementation goes here 
    } 
} 
Cuestiones relacionadas