Konstruktor mit Parametern vererben, workarround?

  • VB.NET

Es gibt 13 Antworten in diesem Thema. Der letzte Beitrag () ist von Nils_Kr.

    Konstruktor mit Parametern vererben, workarround?

    Hi,

    in den meisten Sprachen ist es ja so, dass man einen parametrisierten Konstruktor nicht vererben kann(bei VB auch). Selbst wenn man den Konstruktor der ParentClass aufruft, muss man trotzdem alle Attribute nennen. Jetzt habe ich eine Klassenstruktur mit einem recht umfangreichen Konstruktor in der Oberklasse und bei den Unterklassen variieren nur die Methoden. Da kommt es mir blöde vor immer den Konstruktor von Hand zu kopieren.

    Jetzt habe ich mir überlegt eine Setup-Methode einzurichten, die den Konstruktor ersetzt und die dann vererbt werden kann. Der Konstruktor bleibt leer und das Objekt ist solange gesperrt, bis die Setup-Methode aufgerufen ist.

    Gibt es noch eine elegantere Alternative, und/oder spricht etwas gegen meinen Ansatz?

    Zur Erklärung:
    Aus

    VB.NET-Quellcode

    1. Public MustInherit Class XY
    2. 'attributes [...]
    3. Public Sub New(fkCu As Integer, fkFcg As Integer, lyer As Integer, startd As Date, endd As Date, valType As Integer)
    4. Me.idCu = fkCu
    5. Me.IDFcg = fkFcg
    6. Me.layer = lyer
    7. Me.startD = startd
    8. Me.endD = endd
    9. Me.valueType = valType
    10. If layer > 2 Then canBeDrilled = False
    11. End Sub
    12. End Class


    wird

    VB.NET-Quellcode

    1. Public MustInherit Class XY
    2. 'attributes [...]
    3. Public Sub New()
    4. End Sub
    5. Public Sub setUp(fkCu As Integer, fkFcg As Integer, lyer As Integer, startd As Date, endd As Date, valType As Integer)
    6. Me.idCu = fkCu
    7. Me.IDFcg = fkFcg
    8. Me.layer = lyer
    9. Me.startD = startd
    10. Me.endD = endd
    11. Me.valueType = valType
    12. If layer > 2 Then canBeDrilled = False
    13. End Sub
    14. End Class


    Der Aufruf ist dann statt

    VB.NET-Quellcode

    1. new ObjXy(1,3,4,4534,4, ...)

    einfach so:

    VB.NET-Quellcode

    1. new ObjXy()
    2. ObjXy.setUp(1,3,4,4534,4, ...)

    Dann könnte ich load komplett vererben und müsste nicht jedes mal neu definieren, welche Parameter übergeben werden müssen.
    Option strict = on

    If it's stupid and it works it ain't stupid.

    Dieser Beitrag wurde bereits 8 mal editiert, zuletzt von „Nils_Kr“ ()

    Nils_Kr schrieb:

    Wo kommt die statische Methode hin?
    In dieselbe Klasse.
    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!
    Etwa so:

    VB.NET-Quellcode

    1. Class Class1
    2. Protected Sub New()
    3. End Sub
    4. Public Shared Function CreateInstance(s As [String]) As Class1
    5. Return New Class1()
    6. End Function
    7. End Class


    Edit: Es muss protected sein damit du erben kannst.

    Ich habe noch ein bisschen drüber nach gedacht. Damit kannst du eigentlich auch nicht wirklich weiter kommen. Da die statische Methode vom Typ her nicht passt.


    Das ist meine Signatur und sie wird wunderbar sein!

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Mono“ ()

    So ganz habe ich es leider immernoch nicht geschnallt.

    An welcher Stelle übergebe ich denn dann die Parameter an das Objekt, die ich bei CreateInstance mitgebe?

    VB.NET-Quellcode

    1. Public MustInherit Class ClassXY
    2. Private ATT1 As String
    3. Private ATT2 As Integer
    4. Protected Sub New()
    5. End Sub
    6. Public Shared Function CreateInstance(ATT1 As String, ATT2 As Integer) As ClassXY
    7. Return New ClassXY()
    8. End Function
    9. End Class


    Zusätzlich ist meine Oberklasse abstrakt, da ist Return New ClassXY() nicht zulässig.
    Option strict = on

    If it's stupid and it works it ain't stupid.

    Nils_Kr schrieb:

    Zusätzlich ist meine Oberklasse abstrakt
    So geht das dann natürlich nicht.
    Da musst Du für jede abgeleitete Klasse ein separates CreateInstance() schreiben.

    VB.NET-Quellcode

    1. Public MustInherit Class ClassXY
    2. Private ATT1 As String
    3. Private ATT2 As Integer
    4. Protected Sub New(A1 As String, A2 As Integer)
    5. ATT1 = A1
    6. ATT2 = A2
    7. End Sub
    8. End Class
    9. Public Class ClassXYNew
    10. Inherits ClassXY
    11. Private Sub New(ATT1 As String, ATT2 As Integer)
    12. MyBase.New(ATT1, ATT2)
    13. End Sub
    14. Public Shared Function CreateInstance(ATT1 As String, ATT2 As Integer) As ClassXY
    15. Return New ClassXYNew(ATT1, ATT2)
    16. End Function
    17. End Class

    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:


    Da musst Du für jede abgeleitete Klasse ein separates CreateInstance() schreiben.


    Aber dann bin ich doch wieder bei meinem Ausgangsproblem und könnte genauso für jede Subklasse einen Konstruktor schreiben oder? :D

    Ziel ist es die Parameter, die übergeben werden müssen, nur genau einmal in der Oberklasse zu deklarieren. (Die Attribute sind bei allen Klassen die gleichen)

    Option strict = on

    If it's stupid and it works it ain't stupid.

    Nils_Kr schrieb:

    und könnte genauso für jede Subklasse einen Konstruktor schreiben oder?
    Richtig.
    Lass das CreateInstance() weg und bau Dir entweder zu jeder Klasse mehrere Konstruktoren, die sich dann untereinander aufrufen
    oder
    Du machst das ganze mit Properties, das macht sich bei Erweiterung der Parameter / Properties einfacher.

    VB.NET-Quellcode

    1. Public Class ClassXY
    2. Inherits xyz
    3. Public Sub New(a As Integer, b As Integer, c As Integer)
    4. MyBase.New(a, b, c)
    5. End Sub
    6. Public Sub New(a As Integer, b As Integer)
    7. Me.New(a, b, 7)
    8. End Sub
    9. Public Sub New(a As Integer)
    10. Me.New(a, 5)
    11. End Sub
    12. End Class

    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
    wenn es elegant sein soll, musst du die Parameter im Konstruktor angeben. Was es dir einfacher machen würde, wäre, wenn du eine Structure hernimmst, die die Parameter zusammenfasst.

    Grund dafür ist, dass du die Parameter ja irgendwie an die Klasse geben musst. Du erhöhst die Fehleranfälligkeit, wenn du es über eine (z.B. Overridable) SetParameters-Methode oder dergleichen machst, da der Benutzer gezwungen ist, sich erst über die Handhabung zu informieren und sich strikt an das Protokoll halten muss - welches aber rein logisch nicht nachvollziehbar ist (z.B. b ei TCP ist es logisch, dass man sich vor der Datenübertragung erst verbinden muss, hier scheint es mir unlogisch, dass man erst die Parameter separat angeben muss, damit die Klasse sinnvoll arbeitet).
    Außerdem kann die Methode dann mehrfach aufgerufen werden, außer sie ist privat, was aber keinen Sinn machen würde, weil sie sonst nicht aufrufbar wäre, d.h. der Fehlerbehandlungsaufwand wäre wieder höher.

    Wie gesagt, ich würde es über eine Structure machen, die alle Parameter zusammenfasst.

    Viele Grüße
    ~blaze~
    Ich habe meinen ursprünglichen Ansatz im eingangspost noch etwas erweitert. Meine Idee dahinter ist, dass wenn ich an den Attributen der Oberklasse etwas ändere, dies automatisch für alle Unterklassen und deren Konstruktorersatz gilt. Vielleicht wirkt das etwas unsinnig, aber in meinem Anwendungsfall unterscheiden sich wirklich nur die Methoden.

    Daher nochmal die Frage, gibt es einen Designfehler o.ä. in meinem obigen Ansatz? Mir erschließt es sich nicht, wieso ich parametrisierte Konstruktoren nicht vererben können soll. Aber scheinbar gibt es ja Argumente, die dagegen sprechen, sonst wäre es ja möglich.

    E: Hatte den Beitrag von ~blaze~ noch nicht gesehen.

    E2: @~blaze~ deine Kritikpunkte sind absolut einleuchtend. Was wäre als Structure am geschicktesten? Eine eigene Klasse "Übergabeparameter" wirkt mir irgendwie zu sperrig dafür.
    Option strict = on

    If it's stupid and it works it ain't stupid.

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

    @Nils_Kr So geht das natürlich auch.
    Aber üblicherweise nehmen die Parameter im Laufe der Entwicklung zu, da musste dann immer an mehreren Stellen Hand anlegen.
    Ich füge eine Property hinzu und feddich.
    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!

    Nils_Kr schrieb:

    Was wäre als Structure am geschicktesten? Eine eigene Klasse "Übergabeparameter" wirkt mir irgendwie zu sperrig dafür.


    Eine Struktur mit den Parametern:

    VB.NET-Quellcode

    1. Public Structure BlaParameters
    2. Public Property FkCu As Integer
    3. Public Property FkFcg As Integer
    4. '...
    5. Public Sub New(fkCu As Integer, fkFcg As Integer)
    6. Me.FkCu = fkCu
    7. Me.FkFcg = fkFcg
    8. End Sub
    9. End Structure


    Den Konstruktor musst du halt dann dennoch jedes mal angeben:

    VB.NET-Quellcode

    1. Protected Sub New(parameters As BlaParameters)
    2. '...
    3. End Sub


    In abstrakten Klassen sollte der Konstruktor übrigens Protected sein, laut MSDN-Konventionen (die Klasse ist ja eh nur von ihren Erben aus instanziierbar).

    Der Vorteil, es als Structure zu modellieren ist, dass es ein Wertetyp ist, d.h. es wird quasi identisch zu den Parametern verwaltet: Sie landen auf dem Stapel. Wäre BlaParameters eine Klasse, würde sie im Heap landen und dort überflüssigerweise Speicher verbrauchen und die Vorteile von Klassen (Vererbung, alle Referenzen auf die Instanz verweisen auf die selbe Instanz, usw.) blieben vollständig ungenutzt.
    Man sagt, dass man Strukturen nur bei kleinen Größen verwenden soll (ich glaube, 16 Byte wurde irgendwo mal genannt). Sofern bei dir die Struktur nicht exorbitant groß wird, würde ich mich über die Empfehlung hinwegsetzen. Beachte aber, dass bei einer tiefen Vererbungshierarchie die Struktur jedes mal kopiert wird (sofern der Just-In-Time-Compiler da nicht irgendwas komplett krummes dreht), wenn sie an den Erben weitergereicht wird. Das sollte bei der direkten Parameterangabe aber nicht anders sein. Sonst wäre eine NotInheritable Klasse dann doch die Wahl. Dann müssten aber auch die Properties als ReadOnly-Properties modelliert werden. Strukturen werden stets kopiert, weshalb sich Änderungen an den einzelnen Instanzen nicht auf alle Instanzen beziehen, bei Klassen ist das natürlich nicht der Fall.

    Edit: Ach ja, bzgl. Überschreibbarkeit von Konstruktoren: Das macht in den wenigsten Fällen Sinn. Meist würde das zu Einschränkungen führen. Die Konstruktoren erfordern ja sowieso, dass der Konstruktor des Erben den der Basisklasse aufruft. Würde man eine explizite Signatur vorgeben, wäre das in den meisten Fällen hinderlich (Ausnahme wäre z.B. ISerializable, das meist einen Konstruktor erwartet, da gibt's noch irgendwas mit einer verzögerten Deserialisierung, das hab' ich aber noch nie verwendet).

    Viele Grüße
    ~blaze~
    Danke für die ausführliche Erklärung :thumbup:

    PS: So habe ich es jetzt implemntiert:

    VB.NET-Quellcode

    1. Public Structure TParam
    2. Public FkCu As Integer
    3. Public FkFcg As Integer
    4. Public layer As Integer
    5. Public startd As Date
    6. Public endd As Date
    7. Public valType As Integer
    8. Public Sub New(fkCu As Integer, fkFcg As Integer, layer As Integer, startd As Date, endd As Date, valtype As Integer)
    9. Me.FkCu = fkCu
    10. Me.FkFcg = fkFcg
    11. Me.layer = layer
    12. Me.startd = startd
    13. Me.endd = endd
    14. Me.valType = valtype
    15. End Sub
    16. End Structure
    17. Public MustInherit Class MainClass
    18. Protected Sub New(tp As TParam)
    19. Me.idCu = tp.FkCu
    20. Me.IDFcg = tp.FkFcg
    21. Me.layer = tp.layer
    22. Me.startD = tp.startd
    23. Me.endD = tp.endd
    24. Me.valueType = tp.valType
    25. End Sub
    26. End Class
    27. Public Class SubClass
    28. Public Sub New(tp As TParam)
    29. MyBase.New(tp)
    30. End Sub
    31. End Class
    Option strict = on

    If it's stupid and it works it ain't stupid.

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