Properties ... Fragen über Fragen ...

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

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

    Properties ... Fragen über Fragen ...

    Hi,

    VB.NET-Quellcode

    1. Private myNachname As String
    2. Public Property Nachname() As String
    3. Get
    4. Return myNachname
    5. End Get
    6. Set(value As String)
    7. myNachname = value
    8. End Set
    9. End Property


    Hier habe ich eine Property Nachname ... verwaltet wird der Inhalt über die Private Variable myNachname ... und somit ist die Sache gekapselt. Das verstehe ich soweit ...

    Nun versuche ich das mit einer List(of String)

    VB.NET-Quellcode

    1. Private myNameList As List(Of String)
    2. Public Property NameList As New List(Of String)


    Zunächst mal frage ich mich, warum es jetzt "NameList As List(Of String)" und nicht "NameList() As List(Of String)" heißen muss ... Wieso braucht es denn jetzt die leeren Klammern () nicht mehr? Irgendwie ist das eine andere Syntax ...

    Und dann kann ich die Routinen für "Get" und "Set" nicht mehr anfügen ...

    VB.NET-Quellcode

    1. Public Property NameList As New List(Of String)
    2. Get
    3. End Get
    4. Set(value As List(Of String))
    5. End Set
    6. End Property


    Das Keyword "New" wird bemängelt: "Erweiterte Eigenschaften können nicht initialisiert werden."

    Warum ist das so ? Gilt das generell für nicht primitive Datentypen?

    Ich möchte nun auf die Elemente der List of String lesend und schreibend von außen zugreifen. Das hab ich mal wie folgt versucht:

    VB.NET-Quellcode

    1. Private myNameList As List(Of String)
    2. Public Property NameList As New List(Of String)
    3. From {"Hugo", "Willy", "Frank"}
    4. Public Property Item(ByVal Index As Integer) As String
    5. Get
    6. Return myNameList(Index)
    7. End Get
    8. Set(value As String)
    9. myNameList(Index) = value
    10. End Set
    11. End Property


    Damit handle ich mir schon beim Lesen über "Return myNameList(Index)" eine NullReferenceException ein .

    Bei einem einfachen String klappt das aber.

    VB.NET-Quellcode

    1. Private myNachname As String
    2. Public Property Nachname() As String
    3. Get
    4. Return myNachname
    5. End Get
    6. Set(value As String)
    7. myNachname = value
    8. End Set
    9. End Property


    Was ist denn da jetzt so grundlegend anders?

    Mit diesem Coding klappt das dann auch mit der List(of String)

    VB.NET-Quellcode

    1. Private myNameList As List(Of String)
    2. Public Property NameList As New List(Of String)
    3. From {"Hugo", "Willy", "Frank"}
    4. Public Property Item(ByVal Index As Integer) As String
    5. Get
    6. Return NameList(Index)
    7. End Get
    8. Set(value As String)
    9. NameList(Index) = value
    10. End Set
    11. End Property


    Aber warum ? Wieso kann ich hier den Property Namen statt eines Variablen Namen verwenden? Und wozu brauche ich dann noch die Variable " Private myNameList As List(Of String)"

    Irgendwie fehlt mir da etwas Grundlegendes beim Verständnis der Properties. Ich denke mal, das muss damit zusammenhängen, dass primitive Datentypen keine explizite Instanzierung benötigen, während die ReferenzDatentypen das Zauberwörtchen "New" benötigen. Aber das ist halt nur so eine Ahnung ...

    Hat jemand genügend Geduld mir das (nachsichtig) zu erkären? :)

    LG
    Peter

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

    Properties sind immer so aufgebaut:

    VB.NET-Quellcode

    1. Property Name() As Type
    2. Get
    3. '...
    4. End Get
    5. Set(value As Type)
    6. '...
    7. End Set
    8. End Property

    Als konkretes Beispiel:

    VB.NET-Quellcode

    1. Private _Length As Integer = 3
    2. Public Property Length() As Integer
    3. Get
    4. Return _Length
    5. End Get
    6. Set(value As Integer)
    7. _Length = value
    8. End Set
    9. End Property
    Und folgendes ist die Kurzform davon:

    VB.NET-Quellcode

    1. Public Property Length() As Integer = 3
    Und das kannst Du auch ausprobieren. In Intellisense wird das Feld _Length zwar nicht angezeigt, aber man kann trotzdem drauf zugreifen. Sollte man vermeiden, wenn man nicht genau weiß, was man macht.
    Also die Klammer nach dem Namen der Property sind grundsätzlich immer zu machen.
    Das ist übrigens eine initialisierte Property. Du hast nach dem As Integer noch eine Zuweisung.
    So wäre die Property nicht initialisiert:

    VB.NET-Quellcode

    1. Private _Length As Integer
    2. Public Property Length() As Integer
    3. Get
    4. Return _Length
    5. End Get
    6. Set(value As Integer)
    7. _Length = value
    8. End Set
    9. End Property
    10. 'Bzw. als Kurzform:
    11. Public Property Length() As Integer

    Das gleiche gilt für nicht-primitive Datentypen:

    VB.NET-Quellcode

    1. Private _Foo As MyCustomType
    2. Public Property Foo() As MyCustomType
    3. Get
    4. Return _Foo
    5. End Get
    6. Set(value As MyCustomType)
    7. _Foo = value
    8. End Set
    9. End Property
    10. 'Bzw. als Kurzform:
    11. Public Property Foo() As MyCustomType

    Und für generische Datentypen:

    VB.NET-Quellcode

    1. Private _Items As List(Of T)
    2. Public Property Items() As List(Of T)
    3. Get
    4. Return _Items
    5. End Get
    6. Set(value As List(Of T))
    7. _Items = value
    8. End Set
    9. End Property
    10. 'Bzw. als Kurzform:
    11. Public Property Items() As List(Of T)

    Ich denke, nun sollte klar sein, wie der Zusammenhang zwischen der Kurzform und der "erweiterten" Form besteht. Im Nachfolgenden verwende ich deshalb die Kurzform für die Erklärungen.

    Wie gesagt:

    VB.NET-Quellcode

    1. 'Nicht initialisiert:
    2. Public Property Length() As Integer
    3. 'Initialisiert:
    4. Public Property Length() As Integer = 3

    Und nun kann man die VB-Kurzschreibweisen auch auf Properties anwenden:

    VB.NET-Quellcode

    1. 'Nicht initialisiert:
    2. Public Property Foo() As MyCustomObject
    3. 'Initialisiert:
    4. Public Property Foo() As MyCustomObject = New MyCustomObject()
    5. 'Kurzschreibweise der Initialisierung:
    6. Public Property Foo() As New MyCustomObject
    So wie man es auch bei einer normalen Deklaration schreiben kann:

    VB.NET-Quellcode

    1. Dim Foo As MyCustomObject = New MyCustomObject()
    2. 'Die Kurzform davon:
    3. Dim Foo As New MyCustomObject()

    Beachte, dass auch hier, wenn man es voll ausschreibt, die runden Klammern nach New MyCustomObject kommen. Das ist keine Array-Deklaration, sondern ein Aufruf an eine Methode, die keine Parameter entgegennimmt.

    Hier ein Beispiel für eine indizierte Property. Indizierte Properties können nicht vereinfacht geschrieben werden, weil nicht klar ist, was mit dem Index passieren soll.

    VB.NET-Quellcode

    1. Private _Letters As Char() = "Hallo Welt".ToCharArray() 'Initialisiert
    2. Public Property Letters(Index As Integer) As Char 'Indiziert
    3. Get 'Erweitert
    4. Return _Letters(Index)
    5. End Get
    6. Set(value As Char)
    7. _Letters(Index) = value
    8. End Set
    9. End Property

    Hier wird klar, wofür die Klammer nach dem Property-Namen ist.
    Die gibt ein einziges Zeichen zurück, und zwar das, an der Stelle von Index. Bzw. setzt es.
    Verwendung:

    VB.NET-Quellcode

    1. Dim Test As New MyCustomObject
    2. Dim LetterAt4 As Char = Test.Letters(4)
    3. Test.Letters(2) = "a"c


    Und dann gibt's noch ReadOnly und WriteOnly. Das bedeutet einfach, dass es nur einen Get- bzw. Set-Teil gibt. Ziemlich selbsterklärend. Man kann einer ReadOnly-Property nichts zuweisen, und von einer WriteOnly-Property nichts lesen.

    Und hier ein Beispiel, das Dich glaub ich so verwirrt. Wenn der Typ einer Property ein Array-Typ ist.

    VB.NET-Quellcode

    1. Private _Letters As Char() = "Hallo Welt".ToCharArray()
    2. Public Property Letters() As Char()
    3. Get
    4. Return _Letters
    5. End Get
    6. Set(value As Char())
    7. _Letters = value
    8. End Set
    9. End Property
    Die Property hat hier den Typ "1-dimensionales Array von Char". Und das sagen die Klammern nach dem Typ aus. Man kann auch verschachtelte Arrays verwenden:

    VB.NET-Quellcode

    1. 'Ein Array von Char-Arrays.
    2. Private _LetterLines As Char()() = {New Char() {"H"c, "a"c, "l"c, "l"c, "o"c}, New Char() {"W"c, "e"c, "l"c, "t"c}}
    3. Public Property LetterLines() As Char()()
    4. Get
    5. Return _LetterLines
    6. End Get
    7. Set(value As Char()())
    8. _LetterLines = value
    9. End Set
    10. End Property


    So. Jetzt kann man noch die ganzen VB-Abkürzungen auf Properties anwenden. Z.B. können leere, runde Klammern meistens weggelassen werden. Bei Properties, die nicht indiziert sind und (meistens) bei Methodenaufrufen ohne Parameter, können die runden Klammern nach dem Namen weggelassen werden.
    Also wird es zu:

    VB.NET-Quellcode

    1. Public Property Foo As New MyCustomObject



    Ich möchte nun auf die Elemente der List of String lesend und schreibend von außen zugreifen. Das hab ich mal wie folgt versucht:

    VB.NET-Quellcode

    1. Private myNameList As List(Of String)
    2. Public Property NameList As New List(Of String)
    3. From {"Hugo", "Willy", "Frank"}
    4. Public Property Item(ByVal Index As Integer) As String
    5. Get
    6. Return myNameList(Index)
    7. End Get
    8. Set(value As String)
    9. myNameList(Index) = value
    10. End Set
    11. End Property


    Das Problem solltest Du nun selbst herausgefunden haben.

    VB.NET-Quellcode

    1. Private myNameList As List(Of String)
    Das ist eine ganz einfache Variable in der Klasse.

    VB.NET-Quellcode

    1. Public Property NameList As New List(Of String)
    2. From {"Hugo", "Willy", "Frank"}
    Das aber wird zu folgendem kompiliert:

    VB.NET-Quellcode

    1. Private _NameList As New List(Of String) From {"Hugo", "Willy", "Frank"}
    2. Public Property NameList As List(Of String)
    3. Get
    4. return _NameList
    5. End Get
    6. Set(value As List(Of String))
    7. _NameList = value
    8. End Set
    9. End Property

    Also wenn Du weiter unten auf myNameList zugreifst, ist das was ganz anderes, als das Feld der NameList-Property.
    Wie gesagt:
    Sollte man vermeiden, wenn man nicht genau weiß, was man macht.


    Ich denke mal, das muss damit zusammenhängen, dass primitive Datentypen keine explizite Instanzierung benötigen, während die ReferenzDatentypen das Zauberwörtchen "New" benötigen. Aber das ist halt nur so eine Ahnung ...
    Nicht ganz. Das spielt dann später noch mal eine Rolle, wenn man mit CallByReference arbeitet aber das angesprochene Problem liegt nicht daran.

    Edit:
    Oh, das ist ein ganz schöner Schinken geworden :D
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils

    Peter329 schrieb:

    "NameList As List(Of String)" und nicht "NameList() As List(Of String)"
    Hier hast Du die Syntax von List(Of T) mit der des Arrays() verwechselt.
    Wenn dreist der Zugriff syntaktisch gleich aussieht {val = Element(5)}, ist in der Deklaration das Array durch die Klammern gegeben {Dim Element() As String}, die List hingegen durch das Keyword List(Of T).
    Statt

    Peter329 schrieb:

    VB.NET-Quellcode

    1. Private myNachname As String
    2. Public Property Nachname() As String
    3. Get
    4. Return myNachname
    5. End Get
    6. Set(value As String)
    7. myNachname = value
    8. End Set
    9. End Property
    kannst Du ab Framework 3.5 glaub ich auch gleich

    VB.NET-Quellcode

    1. Public Property Nachname() As String
    schreiben.
    Die Blocksyntax brauchst Du dann nur noch, wenn Du im Getter oder Setter weitere Befehle unterbringen willst.
    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!

    Peter329 schrieb:

    Zunächst mal frage ich mich, warum es jetzt "NameList As List(Of String)" und nicht "NameList() As List(Of String)" heißen muss ... Wieso braucht es denn jetzt die leeren Klammern () nicht mehr? Irgendwie ist das eine andere Syntax ...
    hmm - bei mir geht beides:

    VB.NET-Quellcode

    1. Private _NameList As New List(Of String)
    2. Public Property NameList As List(Of String)
    3. Get
    4. Return _NameList
    5. End Get
    6. Set(value As List(Of String))
    7. _NameList = value
    8. End Set
    9. End Property
    10. Public Property NameList2() As List(Of String)
    11. Get
    12. Return _NameList
    13. End Get
    14. Set(value As List(Of String))
    15. _NameList = value
    16. End Set
    17. End Property
    Beachte: in Vb kannste leere Klammern auch weglassen.
    Beachte auch: Üblicherweise will man keine Liste als öffentliche Property. Also man will schon eine Liste bereitstellen, auch mit Zufügen und löschen, aber es soll keiner hergehen können, und der ListenProperty selbst einen neuen Wert andrehen können oder gar Nothing

    VB.NET-Quellcode

    1. Dim Dings As New DingsMitNameList
    2. Dings.NameList=Nothing ' das meist nix gutt
    Also bedenke, ob du eine öffliche ListenProperty nicht besser Readonly machst

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

    Siehe dazu auch hier.

    in Vb kannste leere Klammern auch weglassen.
    Meistens.
    Z.B. hier nicht:

    VB.NET-Quellcode

    1. Dim a As String = New MyCustomObject().SomeString

    Also wenn man ein neues Objekt instanziiert und davon direkt eine Methode aufruft oder ein Feld liest.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    @Gonger96
    Da muss ich teilweise widersprechen. Ich finds angenehm, wenn man nicht immer die Klammer machen muss. Hat aber eher was damit zu tun, dass die IDE die Klammern nicht automatisch einfügt. Dann wär's mir auch egal. Dagegen tendiert VS sogar, Klammern einzufügen, die da garnicht hingehören. Z.B. wenn man einen Bezeichner eines Feldes alleinstehend in eine Methodenzeile schreibt.
    Ich hab mir mal vorgenommen, in einem Projekt alle Methodenklammern zu machen. Hab's aber noch nicht angefangen, weil's mir einfach zu bequem ist.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    ich finds auch angenehm, wenn ich manchmal das Code-Design ohne weitere Code-Änderungen von Readonly Property auf Function umstellen kann.
    Ich schraube ja ständig am Code-Design herum, und da kommen solche Änderungen garnicht soo selten vor.

    Aber echt Ranz ist, dass die Regel "leere Klammern kannste weglassen" - die ja eiglich schon komisch ist - dass die nicht 100% durchgängig ist.
    Muss man schon sagen.
    @ErfinderDesRades
    dass die Regel "leere Klammern kannste weglassen" [...] nicht 100% durchgängig ist.

    Das ginge bei Konstruktoren auch nicht ohne weiteres:

    VB.NET-Quellcode

    1. Dim a = New Foo.Bar

    Ist da Bar eine in Foo geschachtelte Klasse und wird da eine neue Instanz von Bar erstellt?
    Oder ist Bar eine Instanz-Funktion von Foo und es wird eine neue Instanz von Foo erstellt und daran Bar aufgerufen?
    Das kann man nur feststellen, wenn man den Code von Foo kennt. Es ist also nicht vom aufrufenden Code ersichtlich. In diesem Fall wären die Klammern sehr hilfreich.
    "Luckily luh... luckily it wasn't poi-"
    -- Brady in Wonderland, 23. Februar 2015, 1:56
    Desktop Pinner | ApplicationSettings | OnUtils
    Ich finde C Sprachen dort deutlich schöner. Hinter Funktionsnamen gehören Klammern, ansonsten ists als Delegate zu verstehen. Ob man die jetzt in VB bei parameterlosen Zeugs die Klammern weglassen kann ist Geschmackssache. Ich findes unsinnig, weil die Syntax in sich selbst inkonsequent ist. Imho also wieder so eine Kompatibilitätsgeschichte.
    Erst mal herzlichen Dank an die Ratgeber ... vor allem an Niko, der das alles sehr anschaulich erklärt hat. Ich denke darüber werden sich auch andere im Forum freuen!

    Ich habe jetzt folgendes verstanden:

    Die Klammern nach dem Property Namen sind in VB zwar vorgesehen, können aber weggelassen werden, wohl hauptsächlich deshalb, um arme Menschen wie mich zu verwirren. :)

    Die vereinfachte Schreibweise (auto implementing properties) hab ich auch verstanden. In den Fällen wo es möglich ist, kann man sich die lokale Variable, sowie Getter und Setter (unsichtbar) generieren lassen. Die Variable heißt dann "_PropertyName".

    Die Liste sollte "Private" sein, damit man von außen nur die einzelnen Einträge nicht aber die Liste in Gänze verändern bzw. mit nothing beschädigen kann.

    Da ich jetzt von außen nicht mehr die Anzahl der Listeinträge abfragen kann, muss ich dafür nun eine ReadOnly Eigenschaft NameCount bereitstellen.

    Mein Coding sieht jetzt wie folgt aus:

    VB.NET-Quellcode

    1. Private Property NameList() As New List(Of String)
    2. From {"Hugo", "Willy", "Frank"}
    3. Public ReadOnly Property NameCount() As Integer
    4. Get
    5. Return NameList.Count
    6. End Get
    7. End Property
    8. Public Property Item(ByVal Index As Integer) As String
    9. Get
    10. Return _NameList(Index)
    11. End Get
    12. Set(value As String)
    13. _NameList(Index) = value
    14. End Set
    15. End Property


    NameList ist wegen des Zauberworts "New" eine initialisierte Property. Und für solche Properties kann man offensichtlich nur die "vereinfachte" Syntax verwenden. Wenn ich versuche Getter und Setter selbst anzulegen, gibt es bei "New" einen Syntaxfehler:

    VB.NET-Quellcode

    1. Private Property NameList() As New List(Of String)
    2. From {"Hugo", "Willy", "Frank"}
    3. Get
    4. End Get
    5. Set(value As List(Of String))
    6. End Set
    7. End Property


    Fehler: erweiterte Eigenschaften können nicht initialisiert werden.

    Kann mir jemand erklären warum das so ist? Wieso kann ich jetzt Getter und Setter nicht mehr selbst kodieren?

    LG
    Peter

    Peter329 schrieb:

    Da ich jetzt von außen nicht mehr die Anzahl der Listeinträge abfragen kann, muss ich dafür nun eine ReadOnly Eigenschaft NameCount bereitstellen.
    gar nich wahr!

    VB.NET-Quellcode

    1. dim count = MyObjectMitNameList.NameList.Count


    Peter329 schrieb:

    Fehler: erweiterte Eigenschaften können nicht initialisiert werden.
    Kann mir jemand erklären warum das so ist? Wieso kann ich jetzt Getter und Setter nicht mehr selbst kodieren?
    Nein, das kann glaub keiner erklären.
    Vermutlich technische Probleme, Desinteresse, sie sind noch nicht so weit oder heben sich das für später auf.

    Sieh es optimistisch: Immerhin kann man in VB Auto-Properties auch initialisieren!

    ErfinderDesRades schrieb:

    gar nich wahr!

    VB.NET-Quellcode

    1. dim count = MyObjectMitNameList.NameList.Count



    Also das kann ich leider nicht bestätigen:

    VB.NET-Quellcode

    1. Public Class Form1
    2. Public myKontaktList As New KontaktListe
    3. Private Sub Button3_Click(sender As System.Object, e As System.EventArgs) Handles Button3.Click
    4. For i As Integer = 0 To myKontaktList.NameList.Count -


    liefert bei mir den Fehler:

    Fehler 1 "Bookmark.KontaktListe.Private Property NameList As System.Collections.Generic.List(Of String)" ist in diesem Kontext nicht zugreifbar, da es "Private" ist.
    jau, das hab ich ja auch versucht.

    VB.NET-Quellcode

    1. Public Readonly Property NameList() As New List(Of String)


    Das liefert dann sofort die Meldung "Erweiterte Eigenschaften können nicht initialisiert werden".

    Und ohne "New" ist die Initialisierung mit "From" nicht möglich ...

    Also so wie ich das sehen, kann ich die NameList Property nur instanzieren und initialisieren, wenn das Ding NICHT mit ReadOnly definiert wird. Aber ich kann mich natürlich irren.

    ErfinderDesRades schrieb:

    ach komm - komm doch alleine da drauf, dass Readonly Properties nicht gleichzeitig auch Auto-Properties sein können.


    Wieso sollte ich darauf kommen ? Ich bin halt super naiv und glaube erst mal an die schöne neue objektorientierte Welt, in der alles viel klarer und strukturierter ist. Manchmal stellst du meinen Sinn für Humor schon auf eine harte Probe! :)

    Ok, die Sache mit "Auto Properties" und "New" hab ich jetzt für mich persönlich als nicht so ganz koscher eingeordnet und vermeide das ganz einfach. Egal of ReadOnly oder nicht. Die paar Zeilen Code nehme ich gern in Kauf, wenn die Sache hinterher so funktioniert, wie sie es sollte!

    VB.NET-Quellcode

    1. Dim _NameList As New List(Of String)
    2. Public ReadOnly Property NameList() As List(Of String)
    3. Get
    4. Return _NameList
    5. End Get
    6. End Property


    Und das initiale Befüllen der NameList hab ich in den Konstruktor verlegt. Das klappt dann auch ohne Probleme und ich erspare mir den Umgang mit nicht nachvollziehbaren Kompiler Meldungen.