2010-03-13 14 views
8

He estado haciendo algo de programación últimamente y me enfrenté a un problema que encontré raro en C#. (Al menos para mí)Clase anidada con constructor oculto imposible en C#?

public class Foo 
{ 
    //whatever 
    public class FooSpecificCollection : IList<Bar> 
    { 
     //implementation details 
    } 
    public FooSpecificCollection GetFoosStuff() 
    { 
     //return the collection 
    } 
} 

Quiero que el consumo de Foo para poder obtener una referencia a FooSpecificCollection, incluso realizar algunas operaciones sobre el mismo. Tal vez incluso establecerlo en alguna otra propiedad de Foo o algo así, pero no poder CREAR una instancia de esta clase. (La única clase que debería poder instalar esta colección debería ser Foo.

¿Mi pedido es realmente exagerado? Sé que la gente definió de manera más inteligente C# pero no debería haber tal opción que una clase para padres puede crear una instancia de clase anidada pero nadie más no se puede.

hasta ahora he creado una solución para hacer una clase abstracta, o interfaz disponible a través de la propiedad y poner en práctica una clase privada de concreto que no está disponible en ningún otro lugar.

¿Es esta la manera correcta de manejar una situación así??

+0

posible duplicado: http://stackoverflow.com/questions/1664793/how-to-restrict-access-to-nested-class-member-to-enclosing-class –

+0

Curioso: ¿es el caso de que haya múltiples instancias de 'Foo, cada una con su propia' FooSpecificCollection '? – BillW

+0

Sí, ese es el caso.Digamos que cada cliente tendrá una instancia de Foo (se creará de fábrica en algún momento preciso) y Foo creará una FooSpecificCollection por sí mismo. Doy acceso a una colección específica de foo para que la gente sepa qué foo tiene, y les dejo jugar con los elementos, pero no deberían poder crear uno porque no tiene mucho sentido. Durante la noche pensé que tal vez intenté hacer este código con demasiada semántica :) – luckyluke

Respuesta

4

La manera en que funcionan las clases integradas es que, como miembros de la clase externa, obtienen acceso a los miembros privados de esa clase externa. Pero no al revés (qué es lo que quieres).

Puede proteger el constructor de FooSpecificCollection, pero entonces la fábrica debe ser parte de FooSpecificCollection. Se podría contar con la clase externa:

public class Foo 
{ 
    public class FooSpecificCollection : List<Bar> 
    { 
     private FooSpecificCollection() { } 

     public static FooSpecificCollection GetFoosStuff() 
     { 
      var collection = new FooSpecificCollection(); 
      PrepareFooSpecificCollection(collection); 
      return collection;    
     } 
    } 

    private static void PrepareFooSpecificCollection(FooSpecificCollection collection) 
    { 
     //prepare the collection 
    } 
} 
+0

probablemente tienes razón :) se parece mucho al patrón de fábrica (lo pensé en realidad). – luckyluke

+0

Siempre aprende algo de tus respuestas, gracias, Henk. En este caso, ¿qué sucede si hay varias instancias de 'Foo? – BillW

+1

@BillW, si realmente fuera a usar este patrón probablemente tenga que pasar una instancia de Foo a través de GetFooStuff() y hasta PrepareFooSpecificCollection(). –

3

Si es cre En una biblioteca para que otros la usen, puede hacer que el constructor internal. Cualquier persona fuera de la biblioteca no podrá acceder a ella. Si le preocupa llamar al constructor en su propio proyecto, simplemente no lo llame fuera de la clase principal.

Creamos clases todo el tiempo que no están directamente relacionadas con otras clases, pero los constructores no tienen que estar ocultos de clases no relacionadas. Nosotros (los programadores) sabemos que los objetos no están relacionados, así que nunca creamos una instancia de uno en el otro.

0

No, y realmente no tiene sentido.

Me refiero a que todo el punto es para que pueda devolver otras instancias; pero ¿quién se derivará de esa clase de todos modos? Desde luego, no cualquier otra clase (porque eso estaría mal, e implican no debe estar oculto dentro de la clase principal), por lo ...

+0

Quiero tener alguna funcionalidad en la clase anidada que sea clara y concisa para leer y usar (por ejemplo, una interfaz agradable en algunas colecciones internas de FOO). Y no quiero dejar que las personas creen esa clase. Por supuesto, no deberían tener nada que ver con eso de todos modos, pero aún me gustaría tener una semántica clara que diga: esta clase te ayuda a manejar algo de Foo, pero Foo también te lo da y no deberías crearlo tú solo ... Interno está bien en un ensamblaje pero no entiendo Tu respuesta por qué no tiene sentido. Quiero que un desarrollador use mi clase para no intentar crear una instancia ... ¿por qué es tan absurdo? – luckyluke

+0

Aquí hay otras preocupaciones que las clases derivadas. –

+0

luckyluke: ¿Así que haga que el constructor sea privado? Pensé que estabas tratando de detener a la clase exterior haciendo una instancia de eso. –

4

Haga su clase anidada private y hacen que el valor de retorno de GetFoosStuffIList<Bar> en lugar de FooSpecificCollection .

Además, hay una buena posibilidad de que deriving from List<Bar> is a bug.

+0

Me deriva de una lista no foo – luckyluke

+0

lo siento, quise decir IList por supuesto. La lista sería la implementación interna. Pero de eso no se trata la pregunta. Pero gracias por la vigilancia :) – luckyluke

+1

Simplemente curiosidad por lo que quiere decir con "derivado de' List 'es un error". Miré el enlace, pero no puedo encontrar nada que indique un problema en derivar de 'List ' en sí mismo. –

1

Hay una solución pero no creo que la use en mi aplicación :) La idea es tener una clase derivada de FooSpecific que sea privada y solo se pueda usar dentro de Foo pero tenga un constructor público, entonces Foo puede crear sus instancias

public class Foo 
{ 
    //whatever 
    public class FooSpecific 
    { 
     // Protected contructor. 
     protected FooSpecific() 
     { 
     } 

     // All other code in here. 
    } 

    // Private helper class used for initialization. 
    private class FooSpecificInitHelper : FooSpecific 
    { 
     public FooSpecificInitHelper() 
     { 
     } 
    } 

    // Method in foo to create instaces of FooSpecific. 
    private FooSpecific CreateFooSpecific() 
    { 
     return new FooSpecificInitHelper(); 
    } 
} 
Cuestiones relacionadas