¿Hay alguna manera de hacer que un constructor solo sea visible para una clase principal en C#?

StackOverflow https://stackoverflow.com/questions/20185

  •  09-06-2019
  •  | 
  •  

Pregunta

Tengo una colección de clases que heredan de una clase abstracta que creé.Me gustaría utilizar la clase abstracta como fábrica para crear instancias de implementaciones concretas de mi clase abstracta.

¿Hay alguna forma de ocultar un constructor de todo el código excepto de la clase principal?

Me gustaría hacer esto básicamente.

public abstract class AbstractClass
{
    public static AbstractClass MakeAbstractClass(string args)
    {
        if (args == "a")
            return new ConcreteClassA();
        if (args == "b")
            return new ConcreteClassB();
    }
}

public class ConcreteClassA : AbstractClass
{
}

public class ConcreteClassB : AbstractClass
{
}

Pero quiero evitar que alguien cree directamente una instancia de las 2 clases concretas.Quiero asegurarme de que solo el método MakeAbstractClass() pueda crear instancias de las clases base.¿Hay alguna manera de hacer esto?

ACTUALIZAR
No necesito acceder a ningún método específico de ConcreteClassA o B desde fuera de la clase Abstract.Solo necesito los métodos públicos que proporciona mi clase Abstract.Realmente no necesito evitar que se creen instancias de las clases concretas, solo estoy tratando de evitarlo ya que no proporcionan nuevas interfaces públicas, solo diferentes implementaciones de algunas cosas muy específicas internas a la clase abstracta.

Para mí, la solución más sencilla es hacer clases para niños como mencionó Samjudson.Sin embargo, me gustaría evitar esto ya que haría que el archivo de mi clase abstracta fuera mucho más grande de lo que me gustaría.Prefiero mantener las clases divididas en unos pocos archivos para organizarlas.

Supongo que no hay una solución fácil para esto...

¿Fue útil?

Solución

Puedes hacer que las subclases sean clases secundarias, algo como esto:

public abstract class AbstractClass
{
    public static AbstractClass MakeAbstractClass(string args)
    {
        if (args == "a")
            return new ConcreteClassA();
        if (args == "b")
            return new ConcreteClassB();
    }

    private class ConcreteClassA : AbstractClass
    {
    }

    private class ConcreteClassB : AbstractClass
    {
    }
}

@vaibhav De hecho, esto significa que las clases también están ocultas.Pero, hasta donde yo sé, esta es la única forma de ocultar completamente el constructor.

Editar:Como otros han mencionado, se puede lograr lo mismo usando Reflection, lo que en realidad podría estar más cerca de lo que le gustaría que fuera el caso; por ejemplo, el método anterior responde a que las clases concretas están dentro del mismo archivo que la clase Abstract, lo que probablemente no es muy conveniente.Dicho esto, de esta manera es un buen 'truco', y bueno si el número y la complejidad de las clases concretas es bajo.

Otros consejos

Para mí, la solución más simple es hacer clases infantiles como mencionó samjudson.Me gustaría evitar esto sin embargo, ya que haría que mi archivo de clase abstracta mucho más grande que Me gustaría que lo fuera.Prefiero mantener clases divididas en unos pocos archivos para la organización.

No hay problema, solo usa parcial palabra clave y puede dividir sus clases internas en tantos archivos como desee.No es necesario que lo guardes en el mismo archivo.

Respuesta anterior:

Es posible pero sólo con reflexión.

public abstract class AbstractClass
{
    public static AbstractClass MakeAbstractClass(string args)
    {
        if (args == "a")
            return (AbstractClass)Activator.CreateInstance(typeof(ConcreteClassA), true);
        if (args == "b")
            return (AbstractClass)Activator.CreateInstance(typeof(ConcreteClassB), true);
    }
}

public class ConcreteClassA : AbstractClass
{
    private ConcreteClassA()
    {
    }
}

public class ConcreteClassB : AbstractClass
{
    private ConcreteClassB()
    {
    }
}

y aquí hay otro patrón, sin feo. MakeAbstractClass (argumentos de cadena)

public abstract class AbstractClass<T> where T : AbstractClass<T>
{
    public static T MakeAbstractClass()
    {
        T value = (T)Activator.CreateInstance(typeof(T), true);
        // your processing logic
        return value;
    }
}

public class ConcreteClassA : AbstractClass<ConcreteClassA>
{
    private ConcreteClassA()
    {
    }
}

public class ConcreteClassB : AbstractClass<ConcreteClassB>
{
    private ConcreteClassB()
    {
    }
}

Si las clases están en el mismo ensamblado, ¿no se pueden hacer que los constructores sean internos?

No, no creo que podamos hacer eso.

Siguiendo desde el respuesta aceptada, si tuviera una interfaz pública e hiciera que las clases privadas implementaran la interfaz, podría devolver un puntero a la interfaz y cualquier persona fuera de su clase abstracta principal podría usarlas (sin dejar de ocultar las clases secundarias).

¿De verdad necesidad ¿para hacer esto?Si está utilizando algún tipo de patrón de pseudofábrica sin una verdadera necesidad de diseño, solo hará que su código sea más difícil de entender, mantener y ampliar.

Si no necesita hacer esto, simplemente implemente un patrón de fábrica verdadero.O, más alternativamente, utilice un marco DI/IoC.

¿No puedes usar la palabra clave? partial ¿Para dividir el código de una clase en muchos archivos?

Si está utilizando esta clase en un ensamblaje de servicio independiente, puede utilizar la palabra clave interna.

public class AbstractClass
{
    public AbstractClass ClassFactory(string args)
    {
        switch (args)
        {
            case "A":
                return new ConcreteClassA();               
            case "B":
                return new ConcreteClassB();               
            default:
                return null;
        }
    }
}

public class ConcreteClassA : AbstractClass
{
    internal ConcreteClassA(){ }
}

public class ConcreteClassB : AbstractClass
{
    internal ConcreteClassB() {}
}

Lo que debe hacer es esto para evitar que se cree el constructor predeterminado.El interno se puede cambiar a público si las clases no están en el mismo montaje.

public abstract class AbstractClass{

 public static AbstractClass MakeAbstractClass(string args)
 {
    if (args == "a")
        return ConcreteClassA().GetConcreteClassA();
    if (args == "b")
        return ConcreteClassB().GetConcreteClassB();
 }
}

public class ConcreteClassA : AbstractClass
{
  private ConcreteClassA(){}

  internal static ConcreteClassA GetConcreteClassA(){
       return ConcreteClassA();
  }
}

public class ConcreteClassB : AbstractClass
{
  private ConcreteClassB(){}
  internal static ConcreteClassB Get ConcreteClassB(){
       return ConcreteClassB();
  }

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