PROPERTY

FreeBASIC-Referenz » Alphabetische Befehlsreferenz » P » PROPERTY

Syntax A:

TYPE TypeName
    FeldName [(Indizes)] AS DatenTyp

    ' Wert abfragen
    DECLARE PROPERTY PropertyName ([ [ BYREF | BYVAL ] Index AS DatenTyp]) AS DatenTyp

    ' Wert setzen
    DECLARE PROPERTY PropertyName ([ [ BYREF | BYVAL ] Index AS DatenTyp, ] _
                                     [ BYREF | BYVAL ] NeuerWert AS DatenTyp )
END TYPE

Syntax B:

PROPERTY TypeName.PropertyName [ (Parameterliste) ] AS DatenTyp
   ' Anweisungen
END PROPERTY

Typ: Anweisung
Kategorie: Klassen

PROPERTY erstellt eine Property einer Klasse.
Eine Property kann mit einem UDT-Record (siehe TYPE (UDT)) gleichgesetzt werden; jedoch wird bei jedem Zugriff auf dieses Record ein benutzerdefiniertes Unterprogramm aufgerufen.

Informieren Sie sich zuerst über Prozeduren in FreeBASIC, bevor Sie sich mit dem Thema Properties und Klassen befassen. Siehe dazu auch SUB, FUNCTION.
Ebenso nötig sind Kenntnisse über UDTs (User defined Types)

Dieses Schlüsselwort ist nur zulässig, wenn mit der Kommandozeilenoption -lang fb kompilliert wird..

PROPERTY-Felder werden dazu genutzt, um auf Werte innerhalb eines TYPES zuzugreifen und bei jedem Zugriff eine bestimmte Prozedur aufzurufen.
So, wie sie den Wert eines UDT-Records setzen und abfragen können, können auch Properties gesetzt und abgefragt werden. Die zugehörigen Prozeduren haben dabei denselben Bezeichner; sie unterscheiden sich lediglich durch die Anzahl der Parameter.

Befindet sich der TYPE innerhalb eines NAMESPACEs, so wird die Zuordnung genauso durchgeführt wie bei normalen Prozeduren innerhalb von NAMESPACEs; es ist möglich, den Bezeichner des TYPEs als Präfix vor den Bezeichner der Property zu hängen oder mittels USING (Namespace) das Präfix für den gesamten Code überflüssig machen.

Innerhalb der Prozedur kann auf die übrigen Records des zugeordneten Typs über das Schlüsselwort THIS zugegriffen werden.

Bei der Bearbeitung der Records des Types mit mathematischen Operatoren (+, -, ...) kann das Prinzip "Operator Overloading" angewandt werden; siehe hierzu OPERATOR.

Beispiel 1: Abfragen und Setzen von Werten eines UDTs mittels Properties

Type Vector2D
  As Single x, y
  Declare Operator Cast() As String
  Declare Property Length() As Single
  Declare Property Length( ByVal new_length As Single )
End Type

Operator Vector2D.cast () As String
  Return "(" & x & ", " & y & ")"
End Operator

Property Vector2D.Length() As Single
  Length = Sqr( x * x + y * y )
End Property

Property Vector2D.Length( ByVal new_length As Single )
  Dim m As Single = Length
  If m <> 0 Then
    '' neuer_Vektor = alter_Vektor / laenge * neue_laenge
    x *= new_length / m
    y *= new_length / m
  End If
End Property

Dim a As Vector2D = ( 3, 4 )

Print "a = "; a
Print "a.length = "; a.length
Print

a.length = 10

Print "a = "; a
Print "a.length = "; a.length

Sleep

Ausgabe:

a = (3, 4)
a.length =  5

a = (6, 8)
a.length =  10

Mittels der Schlüsselwörter PRIVATE und PUBLIC kann festgelegt werden, von welchen Programmpunkten aus auf bestimmte Records des UDTs zugegriffen werden darf. PRIVATE-Records können dabei nur von UDT-eigenen Properties angesprochen werden, PUBLIC-Records sind dem gesamten Modul zugänglich. Standardmäßig sind alle Records PUBLIC.
Um festzulegen, welche Records PRIVATE bzw. PUBLIC sind, wird innerhalb einer Typen-Deklaration einfach ein 'PRIVATE:' oder 'PUBLIC:' eingefügt; alle folgenden Elemente folgen der zuletzt genannten Regel.
Innerhalb eines Types kann mehrmals der Status PRIVATE/PUBLIC gewechselt werden:

Beispiel 2: Verwaltung einer Liste mit Properties ohne Möglichkeit für den User, auf die Liste direkt zuzugreifen:

Type MyList
   ' hier gilt noch PUBLIC:
   Declare Constructor ()        ' wird automatisch bei Erstellung aufgerufen
   Declare Destructor  ()        ' wird automatisch bei Beendigung aufgerufen

   Private:                      ' Nur interne Verwendung
      count    As Integer           ' Anzahl der Listenelemente
      adresses As Integer Ptr       ' Pointer auf ein Datenfeld; jedes Element des Feldes zeigt auf einen ZSTRING
      strings  As ZString Ptr       ' Hilfs-Pointer, zum leichteren Umgang mit den ZSTRINGs

   Public:                       ' Für den User
      ' Eigenschaften
      Declare Property Items  (index As Integer)       As String     ' Listenelement Nr. 'index' ausgeben
      Declare Property Items  (index As Integer, value As String)    ' Wert v. Listenelement Nr. 'index' setzen

      ' Methoden
      Declare Sub      AddItem(index As Integer, value As String)    ' Neues Element zur Liste hinzufügen
      Declare Sub      DelItem(index As Integer)                     ' Element Nr. 'index' aus Liste löschen
      Declare Function ItemCount()                     As Integer    ' Anzahl der Listenelemente ausgeben
End Type
'================================================='
Dim As MyList Liste
Dim As Integer i

Liste.Items  (1) = "nein"
Liste.AddItem 2,   "abbrechen"
Liste.AddItem 1,   "ja"
Liste.AddItem 2,   "delete me"

For i = 1 To Liste.ItemCount
   print i, Liste.Items(i)
Next
print

Liste.DelItem 2

For i = 1 To Liste.ItemCount
   print i, Liste.Items(i)
Next

Sleep
'================================================='
Constructor MyList ()
   This.count       = 1
   This.adresses    = CAllocate(1)
   This.strings     = CAllocate( Len("") )
   This.adresses[0] = Cast( Integer, This.strings )
End Constructor
'..............................................................................'
Destructor MyList ()
   Dim As Integer i

   With This
      For i = 1 To .Count
         .Strings = Cast( ZString Ptr, .adresses[i - 1] )
         DeAllocate .Strings
      Next

      DeAllocate .adresses
   End With
End Destructor
'-------------------------------------------------------'
Property MyList.Items(index As Integer) As String
   If (index < 1) Or (index > This.Count) Then Return ""          ' Überprüfe, ob Listenindex existiert;
                                                                  ' Wenn nein, beende Prozedur

   This.Strings = Cast( ZString Ptr, This.adresses[index - 1] )   ' Finde richtige Adresse des Strings
   Return *This.Strings                                           ' Gib gewünschten String zurück
End Property
'..............................................................................'
Property MyList.Items(index As Integer, value As String)
   Dim As Integer i
   Dim As ZString Ptr tmp

   With This
      If (index < 1) Or (index > .Count) Then Return        ' Überprüfe, ob Listenindex existiert;
                                                            ' Wenn nein, beende Prozedur

      .Strings = Cast( ZString Ptr, .adresses[index - 1] )  ' Finde richtige Adresse des Strings
      tmp = ReAllocate( .Strings, Len(value) + 1)           ' Reserviere Speicherplatz für neuen String
      If tmp Then .Strings = tmp Else Return                ' Fahre nur fort, wenn Reservierung erfolgreich

      .adresses[index - 1] = Cast( Integer, .Strings )      ' Speichere neue Adresse

      For i = 0 To Len(value) - 1                           ' Kopiere neuen String in neue Speicherstelle
         .Strings[i] = value[i]
      Next
      .Strings[ Len(value) ] = 0                            ' Setze letztes Element auf 0, da es ein ZSTRING ist

   End With
End Property
'::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::'
Sub MyList.AddItem(index As Integer, value As String)
   Dim As Integer i
   Dim As Integer Ptr tmp

   With This
      If (index < 1) Or (index > .Count + 1) Then Return    ' Überprüfe, ob Listenindex existiert;
                                                            ' Wenn nein, beende Prozedur

      tmp = ReAllocate(.adresses, .Count + 1)               ' reserviere Speicher für eine neue Adresse
      If tmp Then .adresses = tmp Else Return               ' Fahre nur fort, wenn Reservierung erfolgreich

      For i = .Count - 1 To index - 1 Step - 1              ' Verschiebe bisher gespeicherte Adressen i.d. Adress-
         .adresses[i + 1] = .adresses[i]                    ' liste um eins nach vorne, um Platz für das neue
      Next                                                  ' Element zu schaffen
      .count += 1                                           ' Erhöhe den Element-Zähler um eins

      .Strings = Allocate( Len(value) + 1 )                 ' Reserviere Speicherplatz für neuen String

      For i = 0 To Len(value) - 1                           ' Kopiere neuen String in neue Speicherstelle
         .Strings[i] = value[i]
      Next
      .Strings[ Len(value) ] = 0                            ' Setze letztes Element auf 0, da es ein ZSTRING ist

      .adresses[index - 1] = Cast( Integer, .Strings )      ' Speichere die Adresse des neuen Elements
   End With
End Sub
'..............................................................................'
Sub MyList.DelItem(index As Integer)
   Dim As Integer i
   Dim As Integer Ptr tmp

   With This
      If (index < 1) Or (index > .Count) Then Return        ' Überprüfe, ob Listenindex existiert;
                                                            ' Wenn nein, beende Prozedur

      .Strings = Cast( ZString Ptr, .adresses[index - 1] )  ' Lösche den String aus dem Speicher
      DeAllocate .Strings

      tmp = Allocate(.Count - 1)                            ' Erstelle temporäre Kopie der Adress-Liste ohne zu
      For i = 0 To .Count - 1                               ' löschendes Element
         If     i  < index - 1 Then
            tmp[i] = .adresses[i]
         ElseIf i  = index - 1 Then
            ' keine Aktion, da Element gelöscht werden soll!
         Else
            tmp[i - 1] = .adresses[i]
         End If
      Next

      Count -= 1                                            ' aktualisiere Zähler
      DeAllocate .adresses                                  ' Entferne alte Adressliste
      .adresses = tmp                                       ' Übernehme neue Adressliste
   End With
End Sub
'..............................................................................'
Function MyList.ItemCount() As Integer
   Return This.Count
End Function

Ausgabe:

1   ja
2   delete me
3   nein
4   abbrechen

1   ja
2   nein
3   abbrechen

Dieses Beispiel verdeutlicht die Vorteile der PROPERTY-Methode: Zwar ist der Aufwand auf den ersten Blick etwas höher; für jeden denkbaren Zugriff ist eine eigene Prozedur von Nöten. Allerdings kann hierbei gleich festgelegt werden, ob der Zugriff im Lese- und/oder Schreibmodus erfolgen darf; hier z. B. kann jederzeit die Anzahl der Elemente in der Liste abgefragt werden, jedoch kann der Wert nur über .AddItem und .DelItem geändert werden. Außerdem kann automatisch überprüft werden, ob die angegebenen Parameter überhaupt zulässig sind; hier z. B. wird in jeder Property (mit Ausnahme von .ItemCount, das ja keine Parameter besitzt) geprüft, ob sich 'index' im zulässigen Bereich befindet.
Dank der Möglichkeit, einen CONSTRUCTOR zu verwenden, wird die Liste automatisch voll funktionsfähig initiiert, und der DESTRUCTOR sorgt dafür, dass keine Reste im Speicher zurück bleiben.
Und nicht zuletzt ist der Einsatz der erstellten Eigenschaften und Methoden im Modullevel denkbar einfach. Bedenken Sie den Aufwand, dessen es bedarf, um in ein STRING-Array ein Element an beliebiger Stelle einzufügen. Und dabei kann immer noch ein Fehler unterlaufen, wenn Indizes außerhalb des zulässigen Bereichs angegeben werden.

Unterschiede zu QB:
Properties und Methoden existieren nur in VisualBASIC, jedoch existiert dort keine Möglichkeit, solche selbst zu erstellen.

Unterschiede zu früheren Versionen von FreeBASIC: existiert seit FreeBASIC v0.17

Siehe auch:
SUB, FUNCTION, CONSTRUCTOR (Klassen), DESTRUCTOR (Klassen), OPERATOR, THIS, TYPE (UDT), PUBLIC, PRIVATE, OVERLOAD, Objektorientierung