List(Of T) in einer Vererbung überschreiben

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

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von ~blaze~.

    List(Of T) in einer Vererbung überschreiben

    Hallo zusammen,

    ich habe mal wieder eine Frage zu Klassen und Vererbung in VB.NET.

    Ich zeige euch erstmal den Code:

    VB.NET-Quellcode

    1. Public Class Auto
    2. Public Property name As String
    3. Public lst_fahrzeugteile As New List(Of FahrzeugTeil)
    4. End Class
    5. Public Class FahrzeugTeil
    6. Public Property name As String
    7. Public Property seriennummer As String
    8. End Class
    9. Public Class Porsche
    10. Inherits Auto
    11. Public lst_fahrzeugteile As New List(Of ErweitertesFahrzeugTeil)
    12. End Class
    13. Public Class ErweitertesFahrzeugTeil
    14. Inherits FahrzeugTeil
    15. Public Property InterneSeriennummer As String
    16. End Class


    Ich habe eine Klasse "Auto", die wiederrum eine Liste der Klasse "Fahrzeugteile" in sich trägt.
    Außerdem habe ich eine Klasse "Porsche" (erbt von "Auto"), die eine Liste "ErweiterteFahrzeugteile" (erbt von "Fahrzeugteile") enthält.

    Jetzt ist der Plan, dass ich ein Formular für Autos allgemein erstelle.
    Jetzt möchte ich das Formular allerdings auch noch ein Formular für den Porsche haben, welches von dem Formular "Autos" erbt.
    In diesem erweiterten Formular möchte ich mit der Klasse "Porsche" arbeiten, die die Klasse "Auto" aus dem Formular "Autos" überschreiben soll.

    VB.NET-Quellcode

    1. Public Class frm_Porsche
    2. Inherits frm_Auto
    3. Sub New()
    4. InitializeComponent()
    5. cls_auto = New Porsche
    6. End Sub
    7. End Class
    8. Public Class frm_Auto
    9. Public cls_auto As New Auto
    10. Sub New()
    11. InitializeComponent()
    12. End Sub
    13. End Class



    Jetzt ist allerdings das Problem, dass ich nur die Liste der "normalen" Fahrzeugteile und nicht die erweiterte Liste verwenden kann.
    Habt Ihr da Ansätze oder eine Lösung für das Problem?

    Mit freundlichen Grüßen
    Souli ^^
    Ganz spontan:

    Ich würde lst_fahrzeugteile in der Klasse Porsche nicht neu deklarieren wie du es tust, sondern einfach dem von der Klasse Auto bereits geerbten Feld einfach Teile vom Typ ErweitertesFahrzeugTeil zuweisen. (lst_fahrzeugteile bleibt eine List(Of FahrzeugTeil).) Das funktioniert, da ja ein ErweitertesFahrzeugTeil eben auch ein FahrzeugTeil ist.
    Wenn du später auf die Elemente der Liste zugreifst und z.B. auf die nur in ErweitertesFahrzeugTeil verfügbaren Eigenschaften oder Methoden zugreifen willst, prüfst du erst, ob das FahrzeugTeil auch vom Typ ErweitertesFahrzeugTeil ist (obj is ErweitertesFahrzeugTeil) und machst dann ein DirectCast daraus, um auf die Schnittstelle von ErweitertesFahrzeugTeil zugreifen zu können.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Hi
    gibt es einen Grund, warum du dafür separate Forms verwenden musst? Ich würde eher sagen, dass es eine Form gibt, die alle Fahrzeuge verwaltet und ggf. Steuerelemente ein- oder ausblendet.

    Außerdem würde ich übrigens empfehlen, Felder nicht öffentlich zu machen (es gibt ein paar wenige Ausnahmen), sondern Properties zu benutzen, d.h. im Falle der Liste eine ReadOnly-Property.
    Ich würde auch empfehlen, Datenbindung zu verwenden, d.h. z.B. die System.ComponentModel.BindingList(Of T) statt der List(Of T), die du jetzt verwendest. Erstere informiert über Änderungen an der Liste.

    (Was mir auch auffällt, ist, dass du die Dinge nicht einheitlich benennst, das würde ich ändern)

    Viele Grüße
    ~blaze~
    Hallo zusammen,
    sorry, dass ich mich erst jetzt wieder melde.. :)

    @Arby
    Das heißt aber ich müsste jedes mal checken und casten.. :o
    Gibt es keine Möglichkeit diese Liste zu "überschreiben"?
    Sonst muss ich bei jeder Aktion, die ich mit der Klasse ausführe, casten. :/

    @EaranMaleasi
    Eventuell mit der .OfType(Of T) Methode erledigt.

    Diese Methode habe ich noch garnicht gekannt. :)
    Das wäre auch ein interessanter Ansatz, allerdings hat die Liste immer entweder den Typ "FahrzeugTeil" oder den Typ "ErweitertesFahrzeugTeil".
    Eine Mischung aus beiden soll nicht möglich sein.
    Im Endeffekt möchte ich sowa machen:

    VB.NET-Quellcode

    1. List(Of FahrzeugTeil) = List(Of ErweitertesFahrzeugTeil)

    Und ja mir ist vollkommen bewusst, dass das so einfach nicht möglich ist... :)
    Nachdem ich dieses Pseudoüberschreiben ausgeführt habe, muss ich auf die Eigenschaften der erweiterten Klasse ausführen können.

    @'blaze'

    gibt es einen Grund, warum du dafür separate Forms verwenden musst? Ich würde eher sagen, dass es eine Form gibt, die alle Fahrzeuge verwaltet und ggf. Steuerelemente ein- oder ausblendet.

    Ja, es wird nicht nur das Formular Porsche geben, sondern zum Beispiel auf Opel und ich möchte die Formulare nicht überladen.

    Außerdem würde ich übrigens empfehlen, Felder nicht öffentlich zu machen (es gibt ein paar wenige Ausnahmen), sondern Properties zu benutzen, d.h. im Falle der Liste eine ReadOnly-Property.

    Das ist nur ein vereinfachtes Beispiel um den Code kurz und knapp anzuzeigen.
    Aber mit der ReadOnly-Property hast du Recht :)

    Ich würde auch empfehlen, Datenbindung zu verwenden, d.h. z.B. die System.ComponentModel.BindingList(Of T) statt der List(Of T), die du jetzt verwendest. Erstere informiert über Änderungen an der Liste.

    Für die Datenbindung und Datenbankanbindung haben wir eine eigene Mapperklasse, die ebenfalls Funktionen zur Information bei Änderungen beinhaltet.
    Das heißt "List(Of T)" reicht mir also.

    (Was mir auch auffällt, ist, dass du die Dinge nicht einheitlich benennst, das würde ich ändern)

    In wie weit benenne ich nicht einheitlich? :o
    Klassen sind bei mir "cls_*" und Formulare sind "frm_*".
    Properties kann man auch als diese markieren, allerdings sehe ich das nicht als sinnvoll an.

    Mit freundlichen Grüßen
    Souli
    InterneSeriennummer beginnt mit Großbuchstaben, name mit Kleinbuchstaben.
    Ich werde jetzt aber nicht weiter darauf herumtrampeln, ihr habt/du hast da bestimmt eigene Regeln definiert. In der Standard .Net-naming convention werden Typen, Generika und nicht-private Member mit Großbuchstaben benannt.

    Was hältst du davon, dass du die Liste generisch machst? D.h.

    VB.NET-Quellcode

    1. Public Class Auto(Of TFahrzeugteil As Fahrzeugteil)
    2. Public lst_fahrzeugteile As List(Of TFahrzeugteil)
    3. '...


    Du kannst auch zusätzlich dazu ein Interface implementieren, sodass du auch einen nicht-generischen Typen für Auto hast, der nicht mit dem generischen kollidiert.

    Viele Grüße
    ~blaze~
    @~blaze~
    Danke für deine Antwort!! :)
    Das war doch kein Herumtrampeln ;) ein Forum ist ja für den Meinungsaustausch da. :)

    VB.NET-Quellcode

    1. Public Class Auto(Of TFahrzeugteile As FahrzeugTeil)
    2. Property Name As String
    3. Property Lst_Fahrzeugteile As List(Of FahrzeugTeil)
    4. End Class
    5. Public Class FahrzeugTeil
    6. Public Property Name As String
    7. Public Property Seriennummer As String
    8. End Class
    9. Public Class Porsche(Of TFahrzeugteile As ErweitertesFahrzeugTeil)
    10. Inherits Auto(Of TFahrzeugteile)
    11. End Class
    12. Public Class ErweitertesFahrzeugTeil
    13. Inherits FahrzeugTeil
    14. Public Property InterneSeriennummer As String
    15. End Class

    Ich habe das mit der generischen Liste mal gecheckt, allerdings kommt beim Überschreiben der Klasse "Auto" mit dem "Porsche" eine Fehlermeldung der Intellisense.

    VB.NET-Quellcode

    1. Dim cls As Auto(Of FahrzeugTeil)
    2. cls = New Porsche(Of ErweitertesFahrzeugTeil)

    Fehlermeldung: "Der Wert vom Typ "Porsche(Of ErweitertesFahrzeugTeil)" kann nicht in "Auto(Of FahrzeugTeil)" konvertiert werden."

    ~blaze~ schrieb:

    Du kannst auch zusätzlich dazu ein Interface implementieren, sodass du auch einen nicht-generischen Typen für Auto hast, der nicht mit dem generischen kollidiert.

    Nur damit ich es richtig verstehe.. Du möchtest, dass ich aus der Klasse "Auto" ein nicht generisches Interface mache?
    Wenn ja bleibt allerdings immernoch das Problem mit dem Überschreiben des Listen-Typs in dem Formular, oder sehe ich das falsch? ?(
    Das Generikum auf Porsche macht nur Sinn, wenn weitere "Sub-"Porsches von Porsche erben. Du würdest einfach von Auto(Of ErweitertesFahrzeugTeil) erben.
    Wie du bereits bemerkt hast, unterstützt .Net bei Klassen keine Vererbung auf Generika - zumindest bei Typen. Meine Idee wäre, ein Interface zu definieren (dort kannst du auch In/Out-Generika definieren, wie das in VB genau geht, müsste ich nachlesen), sodass du angeben kannst, ob ein generischer Parameter entweder in der Ein- oder Ausgabe seine Erben zulässt. Das ist halt gekoppelt an die Verwendung als Rückgabewert oder Parameter (in C# wären noch out-Parameter wichtig).

    Ansonsten würde man einfach stupide ein Interface IAuto einführen:

    VB.NET-Quellcode

    1. Public Interface IAuto
    2. Function GetFahrzeugteile As IEnumerable(Of Fahrzeugteil) 'oder IList(Of T) oder List(Of T) oder was auch immer
    3. Function GetFahrzeugteilType As Type
    4. 'Auto-Eigenschaften...
    5. End Interface

    du musst halt dann entsprechend die Instanz zu Porsche casten, wenn es ein Porsche ist, kannst aber abstrakt weiterarbeiten.

    Eine weitere Idee wäre, dass du die Properties mit einem Attribut EditableProperty markierst und entsprechend der Property ein Steuerelement generierst. Dazu müsstest du eine Klasse EditablePropertyAttribute, die von Attribute erbt, definieren und über Reflection einige Attribute herausfinden. Das wärde dann so ähnlich wie ein PropertyGrid (welches du übrigens auch verwenden könntest, wenn du das möchtest).
    Alternativ zur Steuerelementgenerierung könntest du die Properties auch als Menge an Eigenschaften zusammenfassen, d.h. du hältst ein Dictionary(Of SomePropertyDescription, SomePropertyReference), wobei SomePropertyDescription z.B. die MemberInfo der Eigenschaft ist, die editiert werden kann (über das EditablePropertyAttribute) und SomePropertyReference einen Wrapper für Getter und Setter der Eigenschaft bilden.
    Da würde ich allerdings die Idee mit dem Interface bevorzugen. Schwierig wird's dort aber ohne den Cast, da kommst du ohne ein derartiges Konstrukt meines Wissens wohl nicht herum.

    Achte übrigens darauf, dass du mal Fahrzeugteil und mal FahrzeugTeil geschrieben hast. ;)

    Viele Grüße
    ~blaze~