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..
- 'TypeName' ist der Name eines UDTs, wie er unter TYPE (UDT) erklärt wird.
- 'Indizes' zeigt an, dass auch Arrays verwendet werden können; wie üblich ist die Anzahl der Dimensionen in diesem Fall aber fest.
- 'FeldName' ist der Name eines UTD-Records. Mindestens ein solcher Record muss existieren, um eine Property einzufügen. Es dürfen selbstverständlich auch mehrere Records verwendet werden.
- 'PropertyName' ist der Name der Property; dieser Bezeichner wird auch verwendet, um das Unterprogramm zu identifizieren, das beim Zugriff auf die Property aufgerufen wird. 'PropertyName' darf auch überladen werden, d.h. es dürfen mehrere Property-Prozeduren mit demselben Bezeichner existieren, die sich dann durch ihre Parameterliste unterscheiden müssen. In manchen Fällen MUSS sogar eine Overloaded Property vorhanden sein; immer wenn der Wert einer Property vom User gesetzt werden kann, muss er auch abfragbar sein. Siehe dazu auch OVERLOAD.
- 'NeuerWert' ist ein Wert, der an die Speicherstelle der Property übergeben werden soll. Er wird an das Unterprogramm übergeben und dort abhängig von den 'Anweisungen' bearbeitet.
- 'Index' wird verwendet, wenn 'FeldName' ein Array ist. Über diesen Parameter kann der Index des Feldelements angegeben werden. Theoretisch ist es auch möglich, hier andere Parameter zu übergeben, z.B. einen Pointer auf eine ganze Struktur von Parametern.
- 'Parameterliste' ist eine Parameterliste, die den Parametern in Syntax A entspricht.
- 'Anweisungen' ist ein Programmcode, der den Regeln einer FUNCTION folgt. Ein Rückgabewert kann mit PROPERTY, 'TypeName.PropertyName' und RETURN gesetzt werden.
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