Existe-t-il un moyen de rendre un constructeur visible uniquement par une classe parent en C# ?

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

  •  09-06-2019
  •  | 
  •  

Question

J'ai une collection de classes qui héritent d'une classe abstraite que j'ai créée.J'aimerais utiliser la classe abstraite comme usine pour créer des instances d'implémentations concrètes de ma classe abstraite.

Existe-t-il un moyen de masquer un constructeur de tout le code à l'exception d'une classe parent.

J'aimerais faire ça en gros

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

Mais je veux empêcher quiconque d'instancier directement les 2 classes concrètes.Je veux m'assurer que seule la méthode MakeAbstractClass() peut instancier les classes de base.Est-ce qu'il y a un moyen de faire ça?

MISE À JOUR
Je n'ai pas besoin d'accéder à des méthodes spécifiques de ConcreteClassA ou B en dehors de la classe Abstract.Je n’ai besoin que des méthodes publiques fournies par ma classe Abstract.Je n'ai pas vraiment besoin d'empêcher l'instanciation des classes Concrete, j'essaie juste de l'éviter car elles ne fournissent pas de nouvelles interfaces publiques, juste des implémentations différentes de certaines choses très spécifiques internes à la classe abstraite.

Pour moi, la solution la plus simple est de faire des cours pour enfants comme Samjudson l'a mentionné.J'aimerais cependant éviter cela, car cela rendrait le fichier de ma classe abstraite beaucoup plus volumineux que je ne le souhaiterais.Je préfère répartir les cours sur quelques fichiers pour l'organisation.

Je suppose qu'il n'y a pas de solution facile à cela...

Était-ce utile?

La solution

Vous pouvez créer des classes enfants pour les sous-classes, quelque chose comme ceci :

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 Cela signifie en effet que les classes sont également masquées.Mais c'est, à ma connaissance, le seul moyen de masquer complètement le constructeur.

Modifier:Comme d'autres l'ont mentionné, la même chose peut être accomplie en utilisant Reflection, ce qui pourrait en fait être plus proche de ce que vous aimeriez que ce soit le cas - par exemple, la méthode ci-dessus répond sur les classes concrètes se trouvant dans le même fichier que la classe Abstract, ce qui est probablement ce n'est pas très pratique.Cela dit, cette méthode est un bon "Hack", et bon si le nombre et la complexité des classes concrètes sont faibles.

Autres conseils

Pour moi, la solution la plus simple est de Créer des classes pour enfants en tant que Samjudson susmentionné.J’aimerais éviter cela Cependant, étant donné que cela rendrait mon abstract class' beaucoup plus grand que J’aimerais qu’il en soit ainsi.Je préfère garder classes réparties sur quelques fichiers pour organisation.

Pas de problème, utilisez simplement partiel mot-clé et vous pouvez diviser vos classes internes en autant de fichiers que vous le souhaitez.Vous n'êtes pas obligé de le conserver dans le même fichier.

Réponse précédente :

C'est possible mais seulement avec réflexion

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

et voici un autre modèle, sans laid MakeAbstractClass (arguments de chaîne)

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 les classes sont dans le même assembly, ne pouvez-vous pas rendre les constructeurs internes ?

Non, je ne pense pas que nous puissions faire ça.

Faisant suite à réponse acceptée, si vous aviez une interface publique et que vous faisiez en sorte que les classes privées implémentent l'interface, vous pourriez alors renvoyer un pointeur vers l'interface et toute personne extérieure à votre classe abstraite parent pourrait alors les utiliser (tout en masquant les classes enfants).

Est-ce que tu réellement besoin pour faire ça?Si vous utilisez une sorte de modèle de pseudo-usine sans véritable besoin de conception, vous ne ferez que rendre votre code plus difficile à comprendre, à maintenir et à étendre.

Si vous n'avez pas besoin de le faire, implémentez simplement un véritable modèle d'usine.Ou, plus ALTy, utilisez un framework DI/IoC.

Tu ne peux pas utiliser le mot-clé partial pour diviser le code d'une classe en plusieurs fichiers ?

Si vous utilisez cette classe dans un assembly de service distinct, vous pouvez utiliser le mot clé 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() {}
}

Ce que vous devez faire, c'est empêcher la création du constructeur par défaut.L'interne peut être changé en public si les classes ne sont pas dans la même assemblée.

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

}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top