CreateInstance, Verhindern des Aufrufs von Konstruktoren abgeleiteter Klassen

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 20 Antworten in diesem Thema. Der letzte Beitrag () ist von RodFromGermany.

    CreateInstance, Verhindern des Aufrufs von Konstruktoren abgeleiteter Klassen

    Moin Leute,
    ich habe in 3 abgeleiteten Klassen verschiedene Algorithmen gekapselt, die ich in mehreren Instanzen einsetzen möchte.
    Um sicherzustellen, dass alle diese Instanzen von genau einer Klasse erstellt werden, möchte ich die Konstruktoren dieser Klassen protected machen und die Instanziierung in einer CreateInstance()-Prozedur vornehmen:
    Spoiler anzeigen

    C#-Quellcode

    1. public class BASE_CLASS
    2. {
    3. protected BASE_CLASS() { }
    4. public static BASE_CLASS CreateInstance()
    5. {
    6. // TODO: Festlegen, welcher Algorithmusverwendet wird
    7. return new CLASS_1(); // hier kommt der Fehler
    8. //return new CLASS_2();
    9. //return new CLASS_3();
    10. }
    11. }
    12. public class CLASS_1 : BASE_CLASS
    13. {
    14. protected CLASS_1() { }
    15. }
    16. public class BASE_CLASS_2 : BASE_CLASS
    17. {
    18. protected BASE_CLASS_2() { }
    19. }
    20. public class CLASS_2 : BASE_CLASS_2
    21. {
    22. protected CLASS_2() { }
    23. }
    24. public class CLASS_3 : BASE_CLASS_2
    25. {
    26. protected CLASS_3() { }
    27. }
    Allerdings kommt in CreateInstance() der Fehler
    'CLASS_1.CLASS_1()' is inaccessible due to its protection level
    Sind die Konstruktoren public, funktioniert das.
    Weiß jemand, wie ich das gestalten muss, damit das so funktioniert, wie es der Plan ist?
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    VB1963 schrieb:

    ist der Fehler weg
    aber ich kann die Konstruktoren immer noch von außen aufrufen:

    C#-Quellcode

    1. class abc
    2. {
    3. BASE_CLASS instance;
    4. public abc()
    5. {
    6. instance = BASE_CLASS.CreateInstance(); // soll gehen
    7. instance = new CLASS_1(); // soll nicht gehen
    8. }
    9. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Class abc
    2. Private instance As BASE_CLASS
    3. Public Sub New()
    4. instance = BASE_CLASS.CreateInstance() ' soll gehen
    5. instance = New CLASS_1() ' soll nicht gehen
    6. End Sub
    7. End Class
    8. Public Class BASE_CLASS
    9. Protected Sub New()
    10. End Sub
    11. Public Shared Function CreateInstance() As BASE_CLASS
    12. ' TODO: Festlegen, welcher Algorithmusverwendet wird
    13. Return New CLASS_1() ' hier kommt der Fehler
    14. 'return new CLASS_2();
    15. 'return new CLASS_3();
    16. End Function
    17. Private Class CLASS_1
    18. Inherits BASE_CLASS
    19. Friend Sub New()
    20. End Sub
    21. End Class
    22. Private Class BASE_CLASS_2
    23. Inherits BASE_CLASS
    24. Friend Sub New()
    25. End Sub
    26. End Class
    27. Private Class CLASS_2
    28. Inherits BASE_CLASS_2
    29. Friend Sub New()
    30. End Sub
    31. End Class
    32. Private Class CLASS_3
    33. Inherits BASE_CLASS_2
    34. Friend Sub New()
    35. End Sub
    36. End Class
    37. End Class

    ...ob's so noch sinnvoll ist - kann ich jetzt nicht beurteilen...

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VB1963“ ()

    VB1963 schrieb:

    beurteilen
    Ja, das funktioniert, aber das Design mit internen Klassen widerstrebt mir ...
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Ich hätte eine abstrakte oder überschreibbare CreateInstance-Methode in die Basisklasse gemacht und anschließend in den speziellen Klassen überschrieben und dort den eigenen Konstruktor aufgerufen.
    So kannst du den Konstruktor theoretisch sogar private machen. :)
    lg.

    LucaWelker

    LucaWelker schrieb:

    in den speziellen Klassen überschrieben
    Jou, das war die richtige Herangehensweise.
    Spoiler anzeigen

    C#-Quellcode

    1. class abc
    2. {
    3. BASE_CLASS instance;
    4. public abc()
    5. {
    6. instance = BASE_CLASS.CreateInstance(); // soll gehen
    7. //instance = new CLASS_1(); // soll nicht gehen
    8. }
    9. }
    10. public class BASE_CLASS
    11. {
    12. protected BASE_CLASS() { }
    13. public static BASE_CLASS CreateInstance()
    14. {
    15. // TODO: Festlegen, welcher Algorithmus verwendet wird
    16. return CLASS_1.CreateClass();
    17. //return CLASS_2.CreateClass();
    18. //return CLASS_3.CreateClass();
    19. }
    20. }
    21. public class CLASS_1 : BASE_CLASS
    22. {
    23. protected internal static BASE_CLASS CreateClass() { return new CLASS_1(); }
    24. protected CLASS_1() { }
    25. }
    26. public class BASE_CLASS_2 : BASE_CLASS
    27. {
    28. protected BASE_CLASS_2() { }
    29. }
    30. public class CLASS_2 : BASE_CLASS_2
    31. {
    32. protected internal static BASE_CLASS CreateClass() { return new CLASS_2(); }
    33. protected CLASS_2() { }
    34. }
    35. public class CLASS_3 : BASE_CLASS_2
    36. {
    37. protected internal static BASE_CLASS CreateClass() { return new CLASS_3(); }
    38. protected CLASS_3() { }
    39. }
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Konstruktoren dieser Klassen protected machen


    Machs lieber private und implementier ein Singelton-Pattern. Dürfte das sein, was du suchst
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

    Radinator schrieb:

    Dürfte das sein, was du suchst
    Nein.

    RodFromGermany schrieb:

    die ich in mehreren Instanzen einsetzen möchte.
    Private geht nicht, da meckert der Compiler.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi
    hätte das entweder über nested types oder über Friend gelöst. Wenn Erben zulässig sein sollen, kann man auch eine statische, interne Methode CreateInstance pro Typ definieren, die den parameterlosen Konstruktor aufruft.
    Nested types sind ok, wenn sie ausschließlich im Typ selbst verwendet werden.
    Ziel von Friend ist es, den Zugriff innerhalb einer Assembly zu behalten. D.h. Verletzungen von außerhalb der in sich geschlossenen Komponente sind generell unterbunden, innerhalb der Assembly aber nicht, da ist der Programmierer dafür zuständig, dass keine ungültigen Aufrufe geschehen. I.A. werden Friend, etc. nur innerhalb eines "Moduls" (nicht mit dem VB-Module oder Assemblymodulen verwechseln, ich meine damit eine komplette Komponente) verwendet, d.h. von außerhalb dürfen keine Aufrufe kommen.

    Viele Grüße
    ~blaze~
    Vielleicht wäre die Nutzung von Reflection auch eine Möglichkeit?
    Spoiler anzeigen

    C#-Quellcode

    1. using System.Reflection;
    2. namespace ConsoleApplication1
    3. {
    4. class Program
    5. {
    6. static void Main(string[] args)
    7. {
    8. var a = Derived.CreateInstance();
    9. a.SaySomething();
    10. Console.ReadLine();
    11. }
    12. }
    13. }
    14. public class BaseClass
    15. {
    16. protected BaseClass()
    17. {
    18. }
    19. public static Derived CreateInstance()
    20. {
    21. return Activator.CreateInstance<Derived>();
    22. }
    23. }
    24. public class Derived : BaseClass
    25. {
    26. public void SaySomething()
    27. {
    28. Console.WriteLine("You have been terminated.");
    29. }
    30. }

    Higlav schrieb:

    Reflection
    ist da doch etwas übertrieben.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    hmm - hab ich jetzt das Problem nicht kapiert, oder warum kommt mir das so einfach vor?

    VB.NET-Quellcode

    1. Public Class Foo
    2. Protected Sub New()
    3. End Sub
    4. Public Shared Function Create(i As Integer) As Foo
    5. If i = 1 Then Return New Foo_1
    6. If i = 2 Then Return New Foo_2
    7. Throw New ArgumentException
    8. End Function
    9. End Class
    10. Public Class Foo_1 : Inherits Foo
    11. End Class
    12. Public Class Foo_2 : Inherits Foo
    13. End Class
    Nach dem Muster sind übrigens zB die Klassen WebRequest, HttpWebRequest, FtpWebRequest architektet.
    ah - aber wie wäre es mit dem hier?

    VB.NET-Quellcode

    1. Public MustInherit Class Foo
    2. Protected Sub InitClass(item As Foo)
    3. End Sub
    4. End Class
    5. Public Class Foo_1 : Inherits Foo
    6. Public Sub New()
    7. InitClass(Me)
    8. End Sub
    9. End Class
    10. Public Class Foo_2 : Inherits Foo
    11. Public Sub New()
    12. InitClass(Me)
    13. End Sub
    14. End Class

    Da kann zwar jeder ein Foo_1, Foo_2 erzeugen, aber der Init-Sub der Basisklasse ist unumgänglich.

    ErfinderDesRades schrieb:

    Da kann zwar jeder ein Foo_1, Foo_2 erzeugen, aber
    das ist eine andere Geschichte, denn der ursprüngliche Plan war, dass an einer Stelle festgelegt sein soll, dass ausschließlich Foo_1, nicht aber Foo_2 bzw. umgekehrt erzeugt werden kann.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Mal nebenbei bemerkt:
    Die (eine mögliche) Lösung steht bereits in Post #7.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!