Vererbung anhand von Konstruktor-Parameter der Basisklasse

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von petaod.

    Vererbung anhand von Konstruktor-Parameter der Basisklasse

    Ich würde gerne anhand eines Konstruktor-Parameters in der Basisklasse entscheiden, welche abgeleitete Klasse die Basisklasse haben soll.
    In etwa so:

    VB.NET-Quellcode

    1. Public Class Provider
    2. Sub New(ProviderType As Integer)
    3. Select Case ProviderType
    4. Case 1 : Me = New Provider1 'Provider soll vom Typ Provider1 sein
    5. Case 2 : Me = New Provider2 'Provider soll vom Typ Provider2 sein
    6. End Select
    7. End Sub
    8. End Class
    9. Public Class Provider1
    10. Inherits Provider
    11. End Class
    12. Public Class Provider2
    13. Inherits Provider
    14. End Class

    Das geht so natürlich nicht.
    Aber vielleicht hat jemand eine brauchbare Idee, wie man während der Initialisierung den späteren Typ setzen kann.

    Oder will ich zuviel und muss die Typabfrage vor dem Initialisieren machen und halt entsprechend New Provider1 bzw. New Provider2 instantiieren?
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Hallo @petaod

    Ich hoffe ich habe das korrekt verstanden.

    z.b.:

    VB.NET-Quellcode

    1. Public Class MyTestClass(Of T As Class)
    2. Public Property TestProperty1 As String
    3. Public Property TestProperty2 As Integer
    4. Public Property TestProperty3 As T
    5. End Class




    Das funktioniert natürlich auch mit Vererbung.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Nicht ganz.
    Dann müsste ich bei der Instantiierung den Namen der abgeleitete Klasse schon angeben.
    Welchen Erben er wählen soll würde ich gerne der Basisklasse überlassen.

    Was mir vorschwebt ist so ein Aufruf:

    VB.NET-Quellcode

    1. ​Dim p As Provider
    2. 'das geht:
    3. p = New Provider1
    4. ' mit deinem Vorschlag würde auch gehen:
    5. p = New Provider(Of Provider1)
    6. 'dafür suche ich eine Lösung:
    7. p = New Provider(1) 'initialisiert mit Provider1
    8. p = New Provider(2) 'initialisiert mit Provider2
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --

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

    @petaod Das geht so nicht, da erst in den Konstruktor der abgeleiteten Klasse eingetreten wird, und von da aus der Konstruktor der Basisklasse aufgerufen wird.
    Wenn Du das ganze in eine Hilfsklasse packst, kannst Du ein Member machen, das parameterabhängig instanziiert wird.
    Die Methoden, Properties und Events musst Du dann antürlich durchreichen.
    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!
    Könntest Du bitte erklären, was Du damit vor hast? Dass die von Dir gezeigte Lösung nicht funktioniert, weißt Du ja. Aber damit wir eine andere Lösung finden können, müssen wir erst mal wissen, was wir eigentlich für ein Problem lösen sollen.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    RodFromGermany schrieb:

    Hilfsklasse
    braucht man nicht.
    Man kann eine PUblic Shared Create(...) As T-Methode in der Basisklasse anlegen, und die kann verschiedenerlei zurückgeben, v.a., wenn sie einen generischen TypParameter hat.

    Aber auch ohne Generica - schau etwas die Klassen-Hierarchie an zwischen WebRequest, HttpWebRequest, FtpWebRequest.

    also mal "praktisch":

    VB.NET-Quellcode

    1. Public Class Provider
    2. Function Create(ProviderType As Integer)As Provider
    3. Select Case ProviderType
    4. Case 1 : return New Provider1 'Provider soll vom Typ Provider1 sein
    5. Case 2 : return New Provider2 'Provider soll vom Typ Provider2 sein
    6. End Select
    7. End Sub
    8. End Class
    9. Public Class Provider1
    10. Inherits Provider
    11. End Class
    12. Public Class Provider2
    13. Inherits Provider
    14. End Class

    Niko Ortner schrieb:

    was Du damit vor hast?

    Ich kriege den Providertyp aus einer XML.
    Abhängig von dem Wert will ich die entsprechende abgeleitete Klasse instantiieren.
    Deshalb benötige ich die entsprechende Übersetzung und die wollte ich halt in der Basisklasse drin haben.

    ErfinderDesRades schrieb:

    Man kann eine PUblic Shared Create(...) As T-Methode in der Basisklasse anlegen
    Genau so habe ich es letztendlich gelöst, allerdings in einer anderen Klasse.
    Auf die Idee, die Funktion in die Basisklasse zu verlagern, bin ich komischerweise gar nicht gekommen.
    Danke.
    Das wird morgen früh die erste Tätigkeit sein, den Code dahin zu verschieben, wo ich ihn haben will.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --
    Aha!
    Da hätte ich folgenden Vorschlag:
    1. Jede abgeleitete Provider-Klasse bekommt ein Attribut, das sie als Provider identifiziert.
    2. Die Provider-Basisklasse hat eine abstrakte Funktion Serialize, die XML zurückgibt. Die einzelnen Provider-Klassen implementieren die wie Du es erwarten würdest.
    3. Jede abgeleitete Provider-Klasse muss einen Konstruktor deklarieren, der XML entgegennimmt. In diesem Konstruktor puhlst Du aus dem XML die nötigen Daten raus. Zu diesem Zeitpunkt weißt Du bereits, um welchen konkreten Typ es sich handelt, weil Du im entsprechenden Konstruktor bist.
    4. In Provider-Basisklasse suchst Du im statischen Konstruktor alle Klassen aus der aktuellen Assembly raus, die von der Provider-Basisklasse abgeleitet sind, die das in Punkt 1 genannte Attribut haben und die den benötigten Konstruktor deklarieren. Diese schmeißt Du in ein Dictionary, das vom Typenname der abgeleiteten Provider-Klasse zum Konstruktor (System.Reflection.ConstructorInfo) ebenjener Klasse mappt.
    5. In der Provider-Basisklasse legst Du eine statische Funktion Deserialize an, die XML entgegennimmt, den Typennamen der abgeleiteten Provider-Klasse aus dem XML rausholt, damit aus dem Dictionary den passenden Konstruktor holt, diesen Aufruft (und dabei das XML an den Konstruktor mitgibt) und das konstruierte Objekt zurückgibt.


    Anmerkungen:
    Das Attribut ist nicht unbedingt nötig. Aber wenn Du mal mehr als eine Vererbungsebene bei den Providern hast, wird es schwierig, herauszufinden, welche Kandidaten es gibt.
    Der Schwachpunkt ist hier, dass man im statischen Konstruktor "manuell" alle Klassen einmal abklappern muss. Die Typen bekommt man ganz gut mit GetType(Provider-Basisklasse).Assembly.GetTypes, aber das beinhaltet halt nur die Assembly, in der die Provider-Basisklasse deklariert ist. Das kann für Dich reichen, aber wenn Du zum Beispiel Plugins verwendest, musst Du einen zusätzlichen Schritt einbauen, um auch da Kandidaten zu finden.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Niko Ortner
    Ja, das wäre ein sehr interessanter Vorschlag.
    Zumal ich Teile der Daten eh schon als Setup XML-serialisiere.
    Aber ich glaube, das ist mir dann doch zu viel des Guten.

    Ich habe mir jetzt eine Create-Function in der Basisklasse angelegt.
    Die Selektion muss ja nicht unbedingt im Konstruktor erfolgen.
    --
    If Not Program.isWorking Then Code.Debug Else Code.DoNotTouch
    --