Existe uma maneira de tornar um construtor visível apenas para uma classe pai em C#?

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

  •  09-06-2019
  •  | 
  •  

Pergunta

Tenho uma coleção de classes que herdam de uma classe abstrata que criei.Gostaria de usar a classe abstrata como uma fábrica para criar instâncias de implementações concretas da minha classe abstrata.

Existe alguma maneira de ocultar um construtor de todo o código, exceto uma classe pai.

Eu gostaria de fazer isso basicamente

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
{
}

Mas quero evitar que alguém instancie diretamente as 2 classes concretas.Quero garantir que apenas o método MakeAbstractClass() possa instanciar as classes base.Há alguma maneira de fazer isso?

ATUALIZAR
Não preciso acessar nenhum método específico de ConcreteClassA ou B de fora da classe Abstract.Eu só preciso dos métodos públicos que minha classe Abstract fornece.Eu realmente não preciso impedir que as classes Concretas sejam instanciadas, estou apenas tentando evitá-lo, já que elas não fornecem novas interfaces públicas, apenas implementações diferentes de algumas coisas muito específicas internas à classe abstrata.

Para mim, a solução mais simples é faça aulas infantis como samjudson mencionou.Eu gostaria de evitar isso, pois tornaria o arquivo da minha classe abstrata muito maior do que eu gostaria.Prefiro manter as aulas divididas em alguns arquivos para organização.

Acho que não há solução fácil para isso...

Foi útil?

Solução

Você pode transformar as subclasses em classes filhas, algo assim:

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 Na verdade, isso significa que as classes também estão ocultas.Mas até onde sei, essa é a única maneira de ocultar completamente o construtor.

Editar:Como outros mencionaram, a mesma coisa pode ser realizada usando Reflection, o que pode estar mais próximo do que você gostaria que fosse - por exemplo, o método acima responde que as classes concretas estão dentro do mesmo arquivo que a classe Abstract, que provavelmente não é muito conveniente.Dito isto, esta forma é um bom 'Hack' e bom se o número e a complexidade das classes concretas forem baixos.

Outras dicas

Para mim, a solução mais simples é fazer as aulas infantis como Samjudson mencionou.Eu gostaria de evitar isso, no entanto, pois tornaria o arquivo da minha classe abstrata muito maior do que eu gostaria que fosse.Prefiro manter as aulas divididas em alguns arquivos para a organização.

Não tem problema, basta usar parcial palavra-chave e você pode dividir suas classes internas em quantos arquivos desejar.Você não precisa mantê-lo no mesmo arquivo.

Resposta anterior:

É possível, mas apenas com reflexão

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()
    {
    }
}

e aqui está outro padrão, sem feio MakeAbstractClass(argumentos de string)

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()
    {
    }
}

Se as classes estiverem no mesmo assembly, não dá para tornar os construtores internos?

Não, não acho que possamos fazer isso.

Na sequência do resposta aceita, se você tivesse uma interface pública e fizesse com que as classes privadas implementassem a interface, você poderia retornar um ponteiro para a interface e qualquer pessoa fora de sua classe abstrata pai poderia usá-los (enquanto ainda ocultava as classes filhas).

Você realmente precisar para fazer isso?Se você estiver usando algum tipo de padrão de pseudo-fábrica sem uma verdadeira necessidade de design, você apenas tornará seu código mais difícil de entender, manter e estender.

Se você não precisar fazer isso, basta implementar um verdadeiro padrão de fábrica.Ou, mais ALTy, use uma estrutura DI/IoC.

Você não pode usar a palavra-chave partial para dividir o código de uma classe em vários arquivos?

Se você estiver usando essa classe em um assembly de serviço separado, poderá usar a palavra-chave internal.

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() {}
}

O que você precisa fazer é evitar que o construtor padrão seja criado.O interno pode ser alterado para público se as classes não estiverem no mesmo assembly.

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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top