Esiste un modo per rendere un costruttore visibile solo a una classe genitore in C#?

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

  •  09-06-2019
  •  | 
  •  

Domanda

Ho una raccolta di classi che ereditano da una classe astratta che ho creato.Mi piacerebbe utilizzare la classe astratta come factory per creare istanze di implementazioni concrete della mia classe astratta.

Esiste un modo per nascondere un costruttore da tutto il codice tranne una classe genitore.

Fondamentalmente mi piacerebbe farlo

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

Ma voglio impedire a chiunque di istanziare direttamente le 2 classi concrete.Voglio assicurarmi che solo il metodo MakeAbstractClass() possa istanziare le classi base.C'è un modo per fare questo?

AGGIORNAMENTO
Non ho bisogno di accedere ad alcun metodo specifico di ConcreteClassA o B dall'esterno della classe Abstract.Ho solo bisogno dei metodi pubblici forniti dalla mia classe Abstract.Non ho davvero bisogno di impedire che le classi Concrete vengano istanziate, sto solo cercando di evitarlo poiché non forniscono nuove interfacce pubbliche, ma solo diverse implementazioni di alcune cose molto specifiche interne alla classe astratta.

Per me la soluzione più semplice è organizza corsi per bambini, come ha detto Samjudson.Vorrei comunque evitarlo poiché renderebbe il file della mia classe astratta molto più grande di quanto vorrei che fosse.Preferirei mantenere le lezioni suddivise in pochi file per l'organizzazione.

Immagino che non ci sia una soluzione semplice a questo...

È stato utile?

Soluzione

Puoi rendere le sottoclassi classi figlie, qualcosa del genere:

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 Ciò significa infatti che anche le classi sono nascoste.Ma questo è, per quanto ne so, l'unico modo per nascondere completamente il costruttore.

Modificare:Come altri hanno già detto, la stessa cosa può essere ottenuta usando Reflection, che in realtà potrebbe essere più vicino a quello che vorresti fosse il caso - ad esempio il metodo sopra risponde alle classi concrete che si trovano all'interno dello stesso file della classe Abstract, che probabilmente non è molto conveniente.Detto questo, in questo modo è un bel "Hack", ed è utile se il numero e la complessità delle classi concrete sono bassi.

Altri suggerimenti

Per me, la soluzione più semplice è quella di fare lezioni di bambini come menzionato Samjudson.Vorrei evitarlo, tuttavia, poiché renderebbe il file della mia classe astratta molto più grande di quanto vorrei che fosse.Preferirei mantenere le lezioni divise su alcuni file per l'organizzazione.

Nessun problema, basta usarlo parziale parola chiave e puoi dividere le tue classi interne in tutti i file che desideri.Non è necessario tenerlo nello stesso file.

Risposta precedente:

È possibile ma solo con la riflessione

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

ed ecco un altro modello, senza brutto MakeAbstractClass(argomenti stringa)

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 le classi si trovano nello stesso assembly, non puoi rendere interni i costruttori?

No, non penso che possiamo farlo.

A seguito del risposta accettata, se avessi un'interfaccia pubblica e facessi in modo che le classi private implementassero l'interfaccia, potresti quindi restituire un puntatore all'interfaccia e chiunque al di fuori della tua classe astratta genitore potrebbe quindi usarli (nascondendo comunque le classi figlie).

Davvero? Bisogno per fare questo?Se stai utilizzando una sorta di modello pseudo-fabbrica senza una reale necessità di progettazione, renderai solo il tuo codice più difficile da comprendere, mantenere ed estendere.

Se non è necessario farlo, implementa semplicemente un vero modello di fabbrica.Oppure, più ALTy, utilizza un framework DI/IoC.

Non puoi usare la parola chiave partial per dividere il codice di una classe in molti file?

Se stai utilizzando questa classe in un assembly di servizio separato, puoi utilizzare la parola chiave 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() {}
}

Ciò che devi fare è impedire la creazione del costruttore predefinito.L'interno può essere cambiato in pubblico se le classi non sono nello stesso 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();
  }

}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top