Gibt es eine Möglichkeit, einen Konstruktor in C# nur für eine übergeordnete Klasse sichtbar zu machen?

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

  •  09-06-2019
  •  | 
  •  

Frage

Ich habe eine Sammlung von Klassen, die von einer von mir erstellten abstrakten Klasse erben.Ich möchte die abstrakte Klasse als Fabrik zum Erstellen von Instanzen konkreter Implementierungen meiner abstrakten Klasse verwenden.

Gibt es eine Möglichkeit, einen Konstruktor vor dem gesamten Code außer einer übergeordneten Klasse zu verbergen?

Ich würde das grundsätzlich gerne machen

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

Aber ich möchte verhindern, dass irgendjemand die beiden konkreten Klassen direkt instanziiert.Ich möchte sicherstellen, dass nur die MakeAbstractClass()-Methode die Basisklassen instanziieren kann.Gibt es eine Möglichkeit, dies zu tun?

AKTUALISIEREN
Ich muss nicht von außerhalb der Abstract-Klasse auf bestimmte Methoden von ConcreteClassA oder B zugreifen.Ich benötige nur die öffentlichen Methoden, die meine Abstract-Klasse bereitstellt.Ich muss nicht wirklich verhindern, dass die Concrete-Klassen instanziiert werden, ich versuche es nur zu vermeiden, da sie keine neuen öffentlichen Schnittstellen bereitstellen, sondern nur unterschiedliche Implementierungen einiger sehr spezifischer Dinge innerhalb der abstrakten Klasse.

Für mich ist die einfachste Lösung Erstellen Sie Kinderklassen, wie von Samjudson erwähnt.Ich möchte dies jedoch vermeiden, da die Datei meiner abstrakten Klasse dadurch viel größer würde, als ich es gerne hätte.Aus Organisationsgründen würde ich die Kurse lieber auf mehrere Dateien verteilen.

Ich denke, dafür gibt es keine einfache Lösung ...

War es hilfreich?

Lösung

Sie können die Unterklassen zu Unterklassen machen, etwa so:

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 Dies bedeutet tatsächlich, dass die Klassen auch ausgeblendet sind.Aber das ist meines Wissens die einzige Möglichkeit, den Konstruktor vollständig auszublenden.

Bearbeiten:Wie andere bereits erwähnt haben, kann das Gleiche mit Reflection erreicht werden, was tatsächlich näher an dem liegt, was Sie gerne hätten – zum Beispiel antwortet die obige Methode darauf, dass sich die konkreten Klassen in derselben Datei wie die Abstract-Klasse befinden, was wahrscheinlich der Fall ist ist nicht sehr praktisch.Allerdings ist dieser Weg ein schöner „Hack“ und gut, wenn die Anzahl und Komplexität der konkreten Klassen gering ist.

Andere Tipps

Für mich besteht die einfachste Lösung darin, Kinderklassen zu erstellen, wie Samjudson erwähnt.Ich möchte dies jedoch vermeiden, da es meine Datei meiner abstrakten Klasse viel größer machen würde, als ich es möchte.Ich möchte lieber die Klassen über einige Dateien für die Organisation aufgeteilt.

Kein Problem, einfach verwenden teilweise Schlüsselwort und Sie können Ihre inneren Klassen in beliebig viele Dateien aufteilen.Sie müssen es nicht in derselben Datei aufbewahren.

Vorherige Antwort:

Es ist möglich, aber nur mit Reflexion

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

und hier ist ein weiteres Muster, ohne hässlich MakeAbstractClass(string args)

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

Wenn sich die Klassen in derselben Assembly befinden, können Sie die Konstruktoren dann nicht intern machen?

Nein, ich glaube nicht, dass wir das schaffen können.

Im Anschluss an die akzeptierte Antwort, Wenn Sie eine öffentliche Schnittstelle hätten und die privaten Klassen die Schnittstelle implementieren ließen, könnten Sie dann einen Zeiger auf die Schnittstelle zurückgeben und jeder außerhalb Ihrer übergeordneten abstrakten Klasse könnte sie dann verwenden (während die untergeordneten Klassen weiterhin ausgeblendet würden).

Tun Sie das tatsächlich? brauchen um dies zu tun?Wenn Sie eine Art Pseudo-Factory-Muster verwenden, ohne dass dafür ein echter Designbedarf besteht, machen Sie Ihren Code nur schwieriger zu verstehen, zu warten und zu erweitern.

Wenn dies nicht erforderlich ist, implementieren Sie einfach ein echtes Factory-Muster.Oder, noch alternativer, verwenden Sie ein DI/IoC-Framework.

Können Sie das Schlüsselwort nicht verwenden? partial zum Aufteilen des Codes für eine Klasse in viele Dateien?

Wenn Sie diese Klasse in einer separaten Service-Assembly verwenden, können Sie das Schlüsselwort internal verwenden.

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

Dies müssen Sie tun, um zu verhindern, dass der Standardkonstruktor erstellt wird.Der interne Wert kann in „öffentlich“ geändert werden, wenn sich die Klassen nicht in derselben Assembly befinden.

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

}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top