AHK Benutzerobjekte und Klassen - Grundbegriffe
Moderator: jNizM
AHK Benutzerobjekte und Klassen - Grundbegriffe
In der Vergangenheit habe ich schon öfter damit begonnen, ein Mini-Tutorium über AHK-Objekte zu schreiben. Und genauso oft habe ich das abgebrochen, weil ich mit dem Ergebnis nicht zufrieden war. Immer wieder bin ich auf das 'Henne/Ei' Problem gestoßen. Das heißt hier, mir ist keine Struktur ohne logische Sprünge eingefallen. Nun habe ich mich doch zu einer 'Veröffentlichung' durchgerungen, obwohl mich das Ergebnis noch immer nicht überzeugt.
Im englischen Forum gibt es bereits einige Tutorials zum Thema Klassen und/oder Objekte. Auch nnnik hat ein Tutorial Beginners OOP with AHK eingestellt. Jeder, der dieses hier liest, sollte als weiterführende Ergänzung auch das lesen. Vielleicht übersetzt nnnik es ins Deutsche, damit die deutschen Mitglieder, deren (Fach-)Englisch nicht so ausgereift ist, etwas mehr davon profitieren können. (Edit: Er ist gerade dabei, s.u. Danke!).
Dieses Tutorium beschränkt sich weitgehend auf Begiffsbestimmungen und soll die AHK-Hilfedatei nur ergänzen. Für viele Detailinformationen und Beispiele muss man nach wie vor die Hilfe bemühen. Ich versuche hier nur, etwas mehr 'Ordnung' in die Dinge zu bringen. Kenntnisse über die Regeln für die Syntax in Ausdrücken (expressions) und über den Umgang mit Funktionen werden vorausgesetzt.
Und weil bei mir die mit AHK 1.1 eingeführte 'Klassensyntax' der Auslöser dafür war, dass ich mich überhaupt mit AHK-Objekten beschäftigt habe, werde ich mich hauptsächlich auf die konzentrieren. Dabei beschränke ich mich auf die Grundfunktionalitäten, ohne auf 'echte' OOP-Spezialitäten einzugehen. Das Tutorium soll in erster Linie den Einstieg in die 'Welt der AHK-Klassen' erleichtern.
Weil ich noch immer nicht überzeugt bin, stelle ich erst einmal eine Art 'unvollständiger Entwurfsfassung' ein. Ich würde gern hören, ob das so überhaupt jemandem hilft, sich den AHK-Objekten und -Klassen anzunähern. Sollte das der Fall sein, werde ich mich bemühen, das Ganze zu vervollständigen. Wenn jemand eine bessere Idee für die Strukturierung hat, werde ich auch gern prüfen, ob ich das umsetzen kann.
Weiterführendes Tutorium von nnnik: OOP:Einsteiger OOP in AHK
Im englischen Forum gibt es bereits einige Tutorials zum Thema Klassen und/oder Objekte. Auch nnnik hat ein Tutorial Beginners OOP with AHK eingestellt. Jeder, der dieses hier liest, sollte als weiterführende Ergänzung auch das lesen. Vielleicht übersetzt nnnik es ins Deutsche, damit die deutschen Mitglieder, deren (Fach-)Englisch nicht so ausgereift ist, etwas mehr davon profitieren können. (Edit: Er ist gerade dabei, s.u. Danke!).
Dieses Tutorium beschränkt sich weitgehend auf Begiffsbestimmungen und soll die AHK-Hilfedatei nur ergänzen. Für viele Detailinformationen und Beispiele muss man nach wie vor die Hilfe bemühen. Ich versuche hier nur, etwas mehr 'Ordnung' in die Dinge zu bringen. Kenntnisse über die Regeln für die Syntax in Ausdrücken (expressions) und über den Umgang mit Funktionen werden vorausgesetzt.
Und weil bei mir die mit AHK 1.1 eingeführte 'Klassensyntax' der Auslöser dafür war, dass ich mich überhaupt mit AHK-Objekten beschäftigt habe, werde ich mich hauptsächlich auf die konzentrieren. Dabei beschränke ich mich auf die Grundfunktionalitäten, ohne auf 'echte' OOP-Spezialitäten einzugehen. Das Tutorium soll in erster Linie den Einstieg in die 'Welt der AHK-Klassen' erleichtern.
Weil ich noch immer nicht überzeugt bin, stelle ich erst einmal eine Art 'unvollständiger Entwurfsfassung' ein. Ich würde gern hören, ob das so überhaupt jemandem hilft, sich den AHK-Objekten und -Klassen anzunähern. Sollte das der Fall sein, werde ich mich bemühen, das Ganze zu vervollständigen. Wenn jemand eine bessere Idee für die Strukturierung hat, werde ich auch gern prüfen, ob ich das umsetzen kann.
Weiterführendes Tutorium von nnnik: OOP:Einsteiger OOP in AHK
Last edited by just me on 20 Feb 2018, 06:28, edited 5 times in total.
Re: AHK Objekte und Klassen - Begrifflichkeiten
Um mir das Editierren zu erleichtern, habe ich das ursprünglich hier eingestellte Tutorium in seine Bestandteile aufgesplittet. An dieser Stelle steht deshalb nur noch ein Inhaltsverzeichnis.
Die einzelnen Teile folgen lückenlos aufeinander. Der Lesefluss sollte deshalb nicht zu sehr gestört werden.
[list][*]Objekte[list][*]Grundlagen
[*]Abgeleitete Objekte (Instanzen)
[*]Vererbung
[*]Datenschutz[/list]
[*]Klassen[list]
[*]Allgemeines und Definition
[*]Daten[list]
[*]Allgemeines
[*]Klassenvariablen
[*]Instanzvariablen[/list]
[*]Methoden[list]
[*]Allgemeines
[*]Konstruktor: __New()
[*]Destruktor: __Delete()[/list]
[*]Eigenschaften (Properties)
[*]Subklassen[/list][/list]
Die einzelnen Teile folgen lückenlos aufeinander. Der Lesefluss sollte deshalb nicht zu sehr gestört werden.
[list][*]Objekte[list][*]Grundlagen
[*]Abgeleitete Objekte (Instanzen)
[*]Vererbung
[*]Datenschutz[/list]
[*]Klassen[list]
[*]Allgemeines und Definition
[*]Daten[list]
[*]Allgemeines
[*]Klassenvariablen
[*]Instanzvariablen[/list]
[*]Methoden[list]
[*]Allgemeines
[*]Konstruktor: __New()
[*]Destruktor: __Delete()[/list]
[*]Eigenschaften (Properties)
[*]Subklassen[/list][/list]
Last edited by just me on 11 Feb 2018, 04:57, edited 14 times in total.
Re: AHK Objekte und Klassen - Begrifflichkeiten
Hi just me,
Ich fand es klasse. Ich konnte es gut lesen, sehr verständlich und ich habe auch etwas gelernt. Wäre super wenn du dies weiterverfolgen würdest.
Ich fand es klasse. Ich konnte es gut lesen, sehr verständlich und ich habe auch etwas gelernt. Wäre super wenn du dies weiterverfolgen würdest.
Re: AHK Objekte und Klassen - Begrifflichkeiten
Hier schon mal ein weiterer dankbarer leser. Habe bereits nnnik's bemühungen mit begeisterung registriert (und daran gedacht, das durch Google-translate zu schicken ) doch so ist es dann für den gemeinen germanischen OOP novizen noch einfacher verdaulich.
Grazie mille
Grazie mille
Re: AHK Objekte und Klassen - Begrifflichkeiten
Ich werde es heute mal durch https://www.deepL.com schicken - es kann sein dass es danach leserlicher als das Englische ist.
Recommends AHK Studio
Re: AHK Objekte und Klassen - Begrifflichkeiten (Baustelle)
Danke, genug des Lobes!
Ich mache dann mal langsam weiter. Für Anregungen, wie man das verbessern i.S.v. 'verständlicher machen' kann, bin ich jederzeit offen. Das gilt auch für Korrekturvorschläge, falls ich etwas falsch dargetellt habe.
Ich mache dann mal langsam weiter. Für Anregungen, wie man das verbessern i.S.v. 'verständlicher machen' kann, bin ich jederzeit offen. Das gilt auch für Korrekturvorschläge, falls ich etwas falsch dargetellt habe.
Re: AHK Objekte und Klassen - Begrifflichkeiten (Baustelle)
Die beiden Tutorials ergänzen sich ziemlich gut - diese Sorte von Grundwissen ist irgendwie Vorraussetzung bei meinem Tutorial.
Aber auf jeden Fall gute Arbeit.
Aber auf jeden Fall gute Arbeit.
Recommends AHK Studio
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Objekte - Grundlagen:
Wichtig: AHK definiert den allen benutzerdefinierten Objekten zugrundeliegenden Typ als "skriptfähiges assoziatives Array". Das betrifft Arrays (einfach und assoziativ), sonstige Objekte und auch Klassen. Hinter den unterschiedlichen Bezeichnungen verbirgt sich immer dieselbe Objektstruktur. Alles, was man mit einem Array machen kann, kann man auch mit sonstigen Objekten und Klassen machen, wie auch umgekehrt. Ich werde im Folgenden noch mehrfach darauf hinweisen.
AHK-Objekte kann man sich als Kapseln vorstellen, wie z.B. die in den berühmten 'Überraschungseiern'. Objekte haben also 'Inhalte', die von außen betrachtet quasi 'unsichtbar' sind. Das sind im wesentlichen Daten und Funktionen. Die Daten werden bei Objekten auch 'Attribute' oder 'Eigenschaften' genannt. Sie sind in internen Variablen abgelegt. Die Funktionen werden bei Objekten 'Methoden' genannt. Die Methoden können auf die internen Daten des Objekts zugreifen, ohne dass die wie sonst bei Funktionen üblich als Parameter übergeben werden müssen.
Im Gegensatz zu den nicht typgebundenen 'normalen' AHK-Variablen, die jederzeit beliebige Inhalte aufnehmen können, müssen AHK Objekte immer ausdrücklich als solche definiert werden. Ein neues 'leeres' Objekt kann mit folgenden gleichwertigen Anweisungen erstellt werden:Daraus ergibt sich schon mal, dass in AHK auch Arrays und Klassen tatsächlich AHK Objekte sind.
In AHK sind Objekte sogenannte 'Prototypen'. Das bedeutet, man kann jedes Objekt direkt nach seiner Erstellung benutzen. In andern Programmiersprachen erzeugt die Definition eines Objekts oft nur eine 'Schablone', die nicht direkt genutzt werden kann. Sie dient dort nur als Vorlage für die Erstellung von lauffähigen 'Instanzen', üblicherweise über ein Schlüsselwort wie New. Erst bei der Erstellung einer 'Instanz' werden die benötigten Speicherbereiche so initialisiert, dass sie nutzbar sind. Auch AHK kennt 'Instanzen', doch dazu später mehr.
Für den Zugriff auf die 'unsichtbaren' Daten und Methoden eines Objekts wird in AHK den Variablen- bzw. Methodennamen der Name des Objekts vorangestellt, z.B.Die Namen sind so 'gekapselt' und können nicht in Konflikt mit gleichen Namen außerhalb des Objekts geraten.
Das Beispiel zeigt, dass es in AHK zwei unterschiedliche Arten des Zugriffs gibt:Dem aufmerksamen Beobachter wird in der Ausgabe __Class=MeinArray auffallen. Dazu später mehr.
Außerdem gibt es in AHK noch eine Reihe reservierter Namen für Methoden, die man selbst definieren kann, um das Standardverhalten der Objekte zu beeinflussen. Die Namen beginnen mit zwei Unterstrichen __ und dienen der Verwaltung der Objekte:
Achtung: Um einen möglichen Konflikt mit reservierten Methodennamen zu vermeiden, sollte man generell keine anderen Methoden definieren, deren Name mit zwei Unterstrichen beginnt.
Wichtig: AHK definiert den allen benutzerdefinierten Objekten zugrundeliegenden Typ als "skriptfähiges assoziatives Array". Das betrifft Arrays (einfach und assoziativ), sonstige Objekte und auch Klassen. Hinter den unterschiedlichen Bezeichnungen verbirgt sich immer dieselbe Objektstruktur. Alles, was man mit einem Array machen kann, kann man auch mit sonstigen Objekten und Klassen machen, wie auch umgekehrt. Ich werde im Folgenden noch mehrfach darauf hinweisen.
AHK-Objekte kann man sich als Kapseln vorstellen, wie z.B. die in den berühmten 'Überraschungseiern'. Objekte haben also 'Inhalte', die von außen betrachtet quasi 'unsichtbar' sind. Das sind im wesentlichen Daten und Funktionen. Die Daten werden bei Objekten auch 'Attribute' oder 'Eigenschaften' genannt. Sie sind in internen Variablen abgelegt. Die Funktionen werden bei Objekten 'Methoden' genannt. Die Methoden können auf die internen Daten des Objekts zugreifen, ohne dass die wie sonst bei Funktionen üblich als Parameter übergeben werden müssen.
Im Gegensatz zu den nicht typgebundenen 'normalen' AHK-Variablen, die jederzeit beliebige Inhalte aufnehmen können, müssen AHK Objekte immer ausdrücklich als solche definiert werden. Ein neues 'leeres' Objekt kann mit folgenden gleichwertigen Anweisungen erstellt werden:
Code: Select all
NeuesObjekt := Array()
NeuesObjekt := [] ; von mir für einfache Arrays bevorzugt
NeuesObjekt := Object()
NeuesObjekt := {} ; von mir für sonstige Objekte bevorzugt
Class NeuesObjekt { ; Klassensyntax
}
In AHK sind Objekte sogenannte 'Prototypen'. Das bedeutet, man kann jedes Objekt direkt nach seiner Erstellung benutzen. In andern Programmiersprachen erzeugt die Definition eines Objekts oft nur eine 'Schablone', die nicht direkt genutzt werden kann. Sie dient dort nur als Vorlage für die Erstellung von lauffähigen 'Instanzen', üblicherweise über ein Schlüsselwort wie New. Erst bei der Erstellung einer 'Instanz' werden die benötigten Speicherbereiche so initialisiert, dass sie nutzbar sind. Auch AHK kennt 'Instanzen', doch dazu später mehr.
Für den Zugriff auf die 'unsichtbaren' Daten und Methoden eines Objekts wird in AHK den Variablen- bzw. Methodennamen der Name des Objekts vorangestellt, z.B.
Code: Select all
ObjektName.VariablenName
; oder
ObjektName["VariablenName"]
Das Beispiel zeigt, dass es in AHK zwei unterschiedliche Arten des Zugriffs gibt:
- Objektsyntax
Der Objektname und der Variablen- bzw. Methodenname werden durch einen Punkt (.) getrennt. Beide Namen werden ausgeschrieben. Das bedeutet, dass der Variablen- bzw. Methodenname den Regeln für Namen entsprechen muss. - Arraysyntax
Der Variablen- bzw. Methodenname wird an den Objektnamen eingeschlossen in eckige Klammern ([ ... ]) angehängt. Die Variablen- bzw. Methodennamen werden in diesem Fall entweder als Zeichenfolge (["Name"]) oder über eine Variable ([VariableMitNamen]) übergeben.
Code: Select all
Class MeinArray{
}
Loop, 3
MeinArray.Push(A_Index)
Msg := "|"
For Key, Value In MeinArray
Msg .= Key . "=" . Value . "|"
MsgBox, %Msg% ; Ergebnis: |1=1|2=2|3=3|__Class=MeinArray|
Außerdem gibt es in AHK noch eine Reihe reservierter Namen für Methoden, die man selbst definieren kann, um das Standardverhalten der Objekte zu beeinflussen. Die Namen beginnen mit zwei Unterstrichen __ und dienen der Verwaltung der Objekte:
- __New() - wird aufgerufen, wenn ein abgeleitetes Objekt (Instanz) erstellt wird
- __Delete() - wird aufgerufen, wenn ein abgeleitetes Object freigegeben bzw. zerstört wird
- __Get() - wird aufgerufen, wenn der Wert einer internen Variable ausgelesen werden soll, die das Objekt nicht kennt
- __Set() - wird aufgerufen, wenn der Wert einer internen Variable geschrieben werden soll, die das Objekt nicht kennt
- __Call() - wird aufgerufen, wenn eine Methode aufgerufen werden soll, die das Objekt nicht kennt
Achtung: Um einen möglichen Konflikt mit reservierten Methodennamen zu vermeiden, sollte man generell keine anderen Methoden definieren, deren Name mit zwei Unterstrichen beginnt.
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Objekte - Abgeleitete Objekte (Instanzen):
Auch in AHK können Objekte als Vorlagen (Basisobjekte) für andere Objekte genutzt werden. Dafür wird z.B. ein neues Objekt mit dem Schlüsselwort New erstellt:Damit wird ein neues eigenständiges Objekt erstellt, das die 'Inhalte' des Basisobjekts übernimmt. Das wird gemeinhin als 'Vererbung' bezeichnet und bedeutet, dass das neue Objekt auf alle Daten und Methoden des Basisobjekts zugreifen kann.
Alternativ geht das auch so:Hier wird ein neues Objekt erstellt, dem sein Basisobjekt über das Schlüsselwort Base untergeschoben wird.
Im Gegensatz dazu weistder Variablen NeuesObjekt lediglich eine weitere Referenz auf das über die Variable BasisObjekt definierte Objekt zu. Das bedeutet, beide Variablen benutzen dasselbe Objekt.
Abgeleitete Objekte werden als 'Instanzen' bezeichnet.
AHK lässt auch zu, Instanzen aus anderen Instanzen abzuleiten:
Achtung: Dabei wird aber nicht Instanz1 zur Basisklasse von Instanz2. Stattdessen wird die Basisklasse von Instanz1 auch zur Basisklasse von Instanz2. Instanz2 erbt deshalb nur die Inhalte von MeineKlasse, nicht die von Instanz1.
Auch in AHK können Objekte als Vorlagen (Basisobjekte) für andere Objekte genutzt werden. Dafür wird z.B. ein neues Objekt mit dem Schlüsselwort New erstellt:
Code: Select all
NeuesObjekt := New BasisObjekt
Alternativ geht das auch so:
Code: Select all
NeuesObjekt := {Base: BasisObjekt}
Im Gegensatz dazu weist
Code: Select all
NeuesObjekt := BasisObjekt
Abgeleitete Objekte werden als 'Instanzen' bezeichnet.
AHK lässt auch zu, Instanzen aus anderen Instanzen abzuleiten:
Code: Select all
Class MeineKlasse {
}
Instanz1 := New MeineKlasse
Instanz2 := New Instanz1
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Objekte - Vererbung:
Wie oben bereits angedeutet, 'erbt' eine Instanz die Inhalte ihres Basisobjekts und kann darauf zugreifen. Wenn man die Inhalte der Instanz aber z.B. mit einer For-Schleife aufzählt (enumeriert), werden die Inhalte des Basisobjekts nicht ausgegeben. Wie kommt die Instanz an diese Inhalte heran?
Damit eine Instanz auf die Inhalte des Basisobjekts zugreifen kann, ist jedes AHK-Objekt mit drei Grundfunktionalitäten ausgestattet. Die Hilfe beschreibt die so:
Man kann das eingebaute Verhalten beeinflussen, indem man diese 'Grundfunktionen' in der Basisklasse durch eigene Methoden ersetzt (überschreibt). Dazu dienen die o.a. reservierten Methodennamen
Achtung: Die Vererbung ist kein statischer Prozess. Damit meine ich, bei der Erstellung einer Instanz wird nicht einfach der Inhalt der Basisklasse in die Instanz kopiert. Die Instanz hält lediglich eine Referenz / einen Verweis auf das Basisobjekt. Nachträgliche Änderungen am Basisobjekt wirken deshalb auch auf alle bereits abgeleiteten Instanzen.
Wie oben bereits angedeutet, 'erbt' eine Instanz die Inhalte ihres Basisobjekts und kann darauf zugreifen. Wenn man die Inhalte der Instanz aber z.B. mit einer For-Schleife aufzählt (enumeriert), werden die Inhalte des Basisobjekts nicht ausgegeben. Wie kommt die Instanz an diese Inhalte heran?
Damit eine Instanz auf die Inhalte des Basisobjekts zugreifen kann, ist jedes AHK-Objekt mit drei Grundfunktionalitäten ausgestattet. Die Hilfe beschreibt die so:
- Get - Abrufen eines Wertes.
- Set - Setzen eines Wertes mit :=.
- Call - Aufrufen einer Methode, gekennzeichnet durch ().
Man kann das eingebaute Verhalten beeinflussen, indem man diese 'Grundfunktionen' in der Basisklasse durch eigene Methoden ersetzt (überschreibt). Dazu dienen die o.a. reservierten Methodennamen
- __Get() - ersetzt die Grundfunktion Get
- __Set() - ersetzt die Grundfunktion Set
- __Call() - ersetzt die Grundfunktion Call
Achtung: Die Vererbung ist kein statischer Prozess. Damit meine ich, bei der Erstellung einer Instanz wird nicht einfach der Inhalt der Basisklasse in die Instanz kopiert. Die Instanz hält lediglich eine Referenz / einen Verweis auf das Basisobjekt. Nachträgliche Änderungen am Basisobjekt wirken deshalb auch auf alle bereits abgeleiteten Instanzen.
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Objekte - Datenschutz:
In AHK sind die Inhalte der Objekte leider weitestgehend ungeschützt. Das bedeutet, man kann die Objekte jederzeit verändern. Das gilt für sowohl für die internen Daten als auch für die Methoden. Den einzigen Schutz bietet die oben angesprochene reservierte Methode __Set(), mit der man das Verhalten beim Hinzufügen neuer Variablen und Methoden steuern kann. Man kann damit z.B. das Hinzufügen verhindern. Leider kann selbst dieser minimale Schutz mit Hilfe der Funktion ObjRawSet restlos ausgehebelt werden. Auch der Aufruf der bereits oben genannten in jedes AHK-Objekt eingebauten Methoden hebelt diesen Minimalschutz aus.
In der Hilfe werden AHK-Objekte als "skriptfähige assoziative Arrays" bezeichnet. Das ist wörtlich zu nehmen und bedeutet, das intern sowohl Variablen wie auch Methoden als "Schlüssel-Wert-Paare" verwaltet werden. Ein Methodenname ist damit ebenfalls ein "Schlüssel" und die Methode kann deshalb per jederzeit gelöscht werden.
Deshalb Achtung: Wenn man eine Klasse schreibt und die z.B. in "Skripte und Funktionen" anderen zur Verfügung stellt, sollte man sich darüber im Klaren sein, dass es keinen Schutz gegen unabsichtliche oder auch absichtliche Veränderungen der Klasse durch das fremde Skript gibt.
In AHK sind die Inhalte der Objekte leider weitestgehend ungeschützt. Das bedeutet, man kann die Objekte jederzeit verändern. Das gilt für sowohl für die internen Daten als auch für die Methoden. Den einzigen Schutz bietet die oben angesprochene reservierte Methode __Set(), mit der man das Verhalten beim Hinzufügen neuer Variablen und Methoden steuern kann. Man kann damit z.B. das Hinzufügen verhindern. Leider kann selbst dieser minimale Schutz mit Hilfe der Funktion ObjRawSet restlos ausgehebelt werden. Auch der Aufruf der bereits oben genannten in jedes AHK-Objekt eingebauten Methoden hebelt diesen Minimalschutz aus.
In der Hilfe werden AHK-Objekte als "skriptfähige assoziative Arrays" bezeichnet. Das ist wörtlich zu nehmen und bedeutet, das intern sowohl Variablen wie auch Methoden als "Schlüssel-Wert-Paare" verwaltet werden. Ein Methodenname ist damit ebenfalls ein "Schlüssel" und die Methode kann deshalb per
Code: Select all
Objekt.Delete("MethodenName")
Deshalb Achtung: Wenn man eine Klasse schreibt und die z.B. in "Skripte und Funktionen" anderen zur Verfügung stellt, sollte man sich darüber im Klaren sein, dass es keinen Schutz gegen unabsichtliche oder auch absichtliche Veränderungen der Klasse durch das fremde Skript gibt.
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Allgemeines und Definition:
Alles, was man mit AHK Klassen machen kann, kann man auch mit 'normalen' AHK Objekten machen. Ich finde aber die sogenannte 'Klassensyntax' für Leute mit weniger OOP Grundwissen (wie z.B. für mich) übersichtlicher bzw. leichter nachvollziehbar.
Äußerlich unterscheiden sich Klassen von anderen Objekten durch die Art der Definition mit der 'Klassensyntax'. Außerdem sind Klassen immer 'super-global'. Das bedeutet, man kann auf sie ohne weiteres an jeder beliebigen Stelle im Skript zugreifen. Bei der Definition einer Klasse wird zudem eine interne Variable __Class angelegt, die den Namen der Klasse aufnimmt.
Definition:
AHK-Klassen werden mit einer eigenen Syntax definiert, der Klassensyntax. Das zugehörige Schlüsselwort ist Class. Damit wird eine Klassendefinition eingeleitet. Diesem Schlüsselwort folgt der Name der Klasse. Darauf folgt ein Codeblock { ... }, der Alles enthält, was zum Klassenobjekt gehört:Damit wird ein neues Klassenobjekt mit dem Namen MeineKlasse erstellt. Dabei wird eine gleichnamige Variable angelegt, die einen Verweis/Zeiger auf das Objekt enthält. Das bedeutet, die Variable MeineKlasse enthält nicht das Objekt selbst, sondern nur einen Zeiger auf den Speicherbereich, in dem das Objekt angelegt wurde. Außerdem wird der Name der Klasse in einer internen Variablen mit dem Namen __Class abgelegt.
Wir haben jetzt eine praktisch leere Klasse, die recht nutzlos ist. Genau genommen haben wir folgendes Objekt erstellt:
Der eigentliche Sinn einer Klasse besteht aber darin, eigene Daten zu verwalten und auf diese über eigene Methoden zugreifen zu können.
Alles, was man mit AHK Klassen machen kann, kann man auch mit 'normalen' AHK Objekten machen. Ich finde aber die sogenannte 'Klassensyntax' für Leute mit weniger OOP Grundwissen (wie z.B. für mich) übersichtlicher bzw. leichter nachvollziehbar.
Äußerlich unterscheiden sich Klassen von anderen Objekten durch die Art der Definition mit der 'Klassensyntax'. Außerdem sind Klassen immer 'super-global'. Das bedeutet, man kann auf sie ohne weiteres an jeder beliebigen Stelle im Skript zugreifen. Bei der Definition einer Klasse wird zudem eine interne Variable __Class angelegt, die den Namen der Klasse aufnimmt.
Definition:
AHK-Klassen werden mit einer eigenen Syntax definiert, der Klassensyntax. Das zugehörige Schlüsselwort ist Class. Damit wird eine Klassendefinition eingeleitet. Diesem Schlüsselwort folgt der Name der Klasse. Darauf folgt ein Codeblock { ... }, der Alles enthält, was zum Klassenobjekt gehört:
Code: Select all
Class MeineKlasse {
}
Wir haben jetzt eine praktisch leere Klasse, die recht nutzlos ist. Genau genommen haben wir folgendes Objekt erstellt:
Code: Select all
Global MeineKlasse := {__Class: "MeineKlasse"}
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Daten - Allgemeines:
Bei den Daten unterscheidet AHK zwischen Klassendaten und Instanzdaten. Klassendaten werden bereits bei der Erstellung der Klasse angelegt. Sie werden mit dem Schlüsselwort Static gekennzeichnet. Wenn man per Schlüsselwort New eine neue Instanz der Klasse erzeugt, werden diese Klassendaten an die Instanz vererbt. Der Instanz werden sie aber in der Regel nur für lesende Zugriffe zur Verfügung gestellt.
Bei den Daten unterscheidet AHK zwischen Klassendaten und Instanzdaten. Klassendaten werden bereits bei der Erstellung der Klasse angelegt. Sie werden mit dem Schlüsselwort Static gekennzeichnet. Wenn man per Schlüsselwort New eine neue Instanz der Klasse erzeugt, werden diese Klassendaten an die Instanz vererbt. Der Instanz werden sie aber in der Regel nur für lesende Zugriffe zur Verfügung gestellt.
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Daten - Klassenvariablen:
Klassenvariablen werden mit dem Schlüsselwort Static gekennzeichnet.Damit wird eine neue Klasse mit Namen MeineKlasse und den Klassenvariablen KlassenVar1 und KlassenVar2 erstellt. Außerdem wird natürlich die interne Variable __Class erstellt und mit dem Klassennamen gefüllt (s.o.).
In Klassenvariablen werden die Werte abgelegt, die die Klasse selbst benötigt, und/oder die, die allen abgeleiteten Klassen (Instanzen) 'vererbt' und dort in der Regel unverändert genutzt werden sollen. Man kann damit für alle Instanzen gültige Werte bereitstellen, für die nur einmal Speicher reserviert werden muss.
Bei der Initialisierung von Klassenvariablen kann man auch auf Werte von vorher definierten Variablen zurückgreifen. Man muss den Variablennamen aber in diesem Fall den Klassennamen voranstellen:
Weil AHK-Klassen als Prototypen vollständig sind, kann man auch direkt über die für die Klasse angelegte Variable auf die Klassenvariablen zugreifen, z.B.
Die an eine Instanz vererbten Klassenvariablen sind gegen Veränderungen durch die Instanz prinzipiell geschützt. Wenn aus einer Instanz heraus versucht wird, in eine Klassenvariable zu schreiben, wird die als Instanzvariable (kommt gleich) in die Instanz kopiert und der neue Wert wird in der Instanz gespeichert. So steht der ursprüngliche Wert anderen Instanzen weiterhin zur Verfügung.
Achtung: Wenn eine Klassenvariable eine Referenz auf ein Objekt enthält, wird auch nur diese Referenz in die Instanzvariable übernommen. Ändert man nun das referenzierte Objekt in der Instanz, wird auch das durch die Klassenvariable referenzierte Objekt verändert, weil beide dasselbe Objekt benutzen. Die Änderung wirkt damit auf alle Instanzen, die anschließend dieses Objekt nutzen wollen oder müssen.
Klassenvariablen werden mit dem Schlüsselwort Static gekennzeichnet.
Code: Select all
Class MeineKlasse {
Static KlassenVar1 := "Wert1"
Static KlassenVar2 := "Wert2"
}
In Klassenvariablen werden die Werte abgelegt, die die Klasse selbst benötigt, und/oder die, die allen abgeleiteten Klassen (Instanzen) 'vererbt' und dort in der Regel unverändert genutzt werden sollen. Man kann damit für alle Instanzen gültige Werte bereitstellen, für die nur einmal Speicher reserviert werden muss.
Bei der Initialisierung von Klassenvariablen kann man auch auf Werte von vorher definierten Variablen zurückgreifen. Man muss den Variablennamen aber in diesem Fall den Klassennamen voranstellen:
Code: Select all
Class MeineKlasse {
Static KlassenVar1 := "Hallo"
Static KlassenVar2 := MeineKlasse.KlassenVar1 . " Welt"
}
Code: Select all
Wert := MeineKlasse.KlassenVar1
Achtung: Wenn eine Klassenvariable eine Referenz auf ein Objekt enthält, wird auch nur diese Referenz in die Instanzvariable übernommen. Ändert man nun das referenzierte Objekt in der Instanz, wird auch das durch die Klassenvariable referenzierte Objekt verändert, weil beide dasselbe Objekt benutzen. Die Änderung wirkt damit auf alle Instanzen, die anschließend dieses Objekt nutzen wollen oder müssen.
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Daten - Instanzvariablen:
Instanzvariablen sind interne Variablen, die nur innerhalb genau einer Instanz gültig sind. Sie können in der Basisklasse vordefiniert werden, werden aber erst dann erstellt, wenn eine neue Instanz erstellt wird. Intern werden sie dazu in einer Funktion/Methode mit dem vordefinierten Namen __Init() gesammelt. Schon an den einleitenden Unterstrichen erkennt man, dass die zu den reservierten Verwaltungsmethoden gehört. Sie wird aufgerufen, wenn eine neue Instanz erstellt wird. Innerhalb der Basisklasse stehen diese Variablen nicht zur Verfügung. Die Namen der vordefinierten Instanzvariablen müssen sich aber von denen der Klassenvariablen unterscheiden, sonst gibt es einen Fehler wegen doppelter Definitionen.
In der Definition der Basisklasse werden diese Variablen ohne das Schlüsselwort Static definiert. Das sieht dann so aus:
Wenn nun mit
eine neue Instanz erstellt wird, verfügt die automatisch über 4 interne Variablen mit vorbelegten Werten :
Man kann Klassen jederzeit sowohl von Innen, das bedeutet, aus den eingebauten Methoden heraus, als auch von Außen neue Variablen hinzufügen, z.B. mitDie so erstellten Variablen werden automatisch zu Instanzvariablen, sind also nur innerhalb einer konkreten Instanz vorhanden.
Achtung: Macht man das mit einer Basisklasse, können allerdings Probleme entstehen, weil die 'Instanzvariablen' einer Basisklasse die 'Klassenvariablen' sind. Die Änderung der Basisklasse kann deshalb auf abgeleitete Instanzen durchschlagen.
Instanzvariablen sind interne Variablen, die nur innerhalb genau einer Instanz gültig sind. Sie können in der Basisklasse vordefiniert werden, werden aber erst dann erstellt, wenn eine neue Instanz erstellt wird. Intern werden sie dazu in einer Funktion/Methode mit dem vordefinierten Namen __Init() gesammelt. Schon an den einleitenden Unterstrichen erkennt man, dass die zu den reservierten Verwaltungsmethoden gehört. Sie wird aufgerufen, wenn eine neue Instanz erstellt wird. Innerhalb der Basisklasse stehen diese Variablen nicht zur Verfügung. Die Namen der vordefinierten Instanzvariablen müssen sich aber von denen der Klassenvariablen unterscheiden, sonst gibt es einen Fehler wegen doppelter Definitionen.
In der Definition der Basisklasse werden diese Variablen ohne das Schlüsselwort Static definiert. Das sieht dann so aus:
Code: Select all
Class BasisKlasse {
; Klassenvariablen - mit Static
Static KlassenVar1 := "Wert1"
Static KlassenVar2 := "Wert2"
; Instanzvariablen - ohne Static
InstanzVar1 := "Wert4"
InstanzVar2 := "Wert5"
}
Code: Select all
Instanz := New BasisKlasse
- 2 Klassenvariablen, die sich diese Instanz mit anderen Instanzen teilt
- 2 Instanzvariablen, die dieser Instanz allein gehören, und mit denen sie machen kann, was sie will, ohne dass andere Instanzen davon betroffen sein können.
Man kann Klassen jederzeit sowohl von Innen, das bedeutet, aus den eingebauten Methoden heraus, als auch von Außen neue Variablen hinzufügen, z.B. mit
Code: Select all
Instanz.NeueVar := "Wert" ; Objekt-Syntax
; oder
Instanz["NeueVar"] := "Wert" ; Array-Syntax
Achtung: Macht man das mit einer Basisklasse, können allerdings Probleme entstehen, weil die 'Instanzvariablen' einer Basisklasse die 'Klassenvariablen' sind. Die Änderung der Basisklasse kann deshalb auf abgeleitete Instanzen durchschlagen.
Last edited by just me on 11 Feb 2018, 07:29, edited 1 time in total.
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Methoden - Allgemeines:
Methoden sind im Printip in der Klasse enthaltene Funktionen, die auf die Klassendaten zugreifen können, ohne dass die als Parameter übergeben werden müssen. Man kann den Methoden aber wie normalen Funktionen zusätzliche Parameter übergeben. Die Definition sieht wie eine normale Funktionsdefinition aus, sie ist aber in die Klassendefiniton eingebettet:Beim Aufruf wird - wie bei den Variablen - der Objektname gefolgt von einem Punkt dem Methodennamen vorangestellt. Dem Methodennamen folgen direkt öffnende und schließende Klammern, zwischen denen Parameter angegeben werden können:
Wie kommt nun die Methode an die Klassendaten?
Dafür wird den Objektmethoden als erster Parameter immer eine Referenz auf das Objekt übergeben. Wir betrachten dafür erst einmal, wie die vorstehende Klassendefinition in 'normaler' Objektsyntax aussehen würde:Man kann so diesen ersten Parameter klar erkennen.
Bei Klassenmethoden wird das anders gehandhabt. AHK spricht hier von einem "versteckten" oder "verborgenen" Parameter. Ich hatte damit zunächst Vertändnisprobleme, habe dann aber für mich folgende Erklärung gefunden:
Beim Aufruf von Klassenmethoden entfernt AHK den ersten übergebenen Parameter aus der Parameterliste und stellt ihn innerhalb der Methode über den reservierten Variablennamen This zur Verfügung. Der erste Parameter darf deshalb in der Methodendefinition nicht angegeben werden.
Damit kann man innerhalb von Methoden über die Variable This auf die Instanzvariablen zugreifen oder auch andere Klassenmethoden aufrufen:This ersetzt hier den Objektnamen.
Problematisch wird dieses Verhalten aber, wenn man eine Objektmethode z.B. als Funktion für eine OnMessage() Routine oder eine Callbackfunktion nutzen will. Auch in diesem Fall wird der erste Parameter aus der Liste entfernt und darf deshalb nicht als Funktionsparameter angegeben werden. Er enthält aber dann nicht die Objektreferenz, sondern einen 'normalen' Funktionsparameter. Die Variable This enthält deshalb innerhalb der Methode den Wert des entfernten Funktionsparameters und man kann über sie nicht auf das Methodenobjekt zugreifen.
Man kann dieses 'Fehlverhalten' über die Funktion ObjBindMethod(Objekt, "MethodenName") 'korrigieren'. So wird den 'normalen' Funktionsparamatern wieder die Objektreferenz vorangestellt. Das hat allerdings den Nachteil, dass eine zweite Referenz auf das Objekt erstellt wird, was beim Freigeben des Objekts zu Problemen führen kann. Doch dazu später mehr.
Methoden sind im Printip in der Klasse enthaltene Funktionen, die auf die Klassendaten zugreifen können, ohne dass die als Parameter übergeben werden müssen. Man kann den Methoden aber wie normalen Funktionen zusätzliche Parameter übergeben. Die Definition sieht wie eine normale Funktionsdefinition aus, sie ist aber in die Klassendefiniton eingebettet:
Code: Select all
Class MeineKlasse {
Static KlassenVar := "KlassenVar"
Methode() {
;... Befehle
}
}
Code: Select all
MeineKlasse.Methode()
Dafür wird den Objektmethoden als erster Parameter immer eine Referenz auf das Objekt übergeben. Wir betrachten dafür erst einmal, wie die vorstehende Klassendefinition in 'normaler' Objektsyntax aussehen würde:
Code: Select all
Global MeineKlasse := {KlassenVar: "KlassenVar", Methode: Func("Methode")}
Methode(Objektreferenz, Param1, Param2, ...) {
...
}
Bei Klassenmethoden wird das anders gehandhabt. AHK spricht hier von einem "versteckten" oder "verborgenen" Parameter. Ich hatte damit zunächst Vertändnisprobleme, habe dann aber für mich folgende Erklärung gefunden:
Beim Aufruf von Klassenmethoden entfernt AHK den ersten übergebenen Parameter aus der Parameterliste und stellt ihn innerhalb der Methode über den reservierten Variablennamen This zur Verfügung. Der erste Parameter darf deshalb in der Methodendefinition nicht angegeben werden.
Damit kann man innerhalb von Methoden über die Variable This auf die Instanzvariablen zugreifen oder auch andere Klassenmethoden aufrufen:
Code: Select all
Class MeineKlasse {
Static KlassenVar := "KlassenVar"
Methode1() {
Var1 := This.Klassenvar
Var2 := This.Methode2()
MsgBox, Var1: %Var1% - Var2: %Var2%
}
Methode2() {
Static Var := 7
Return Var
}
}
Problematisch wird dieses Verhalten aber, wenn man eine Objektmethode z.B. als Funktion für eine OnMessage() Routine oder eine Callbackfunktion nutzen will. Auch in diesem Fall wird der erste Parameter aus der Liste entfernt und darf deshalb nicht als Funktionsparameter angegeben werden. Er enthält aber dann nicht die Objektreferenz, sondern einen 'normalen' Funktionsparameter. Die Variable This enthält deshalb innerhalb der Methode den Wert des entfernten Funktionsparameters und man kann über sie nicht auf das Methodenobjekt zugreifen.
Man kann dieses 'Fehlverhalten' über die Funktion ObjBindMethod(Objekt, "MethodenName") 'korrigieren'. So wird den 'normalen' Funktionsparamatern wieder die Objektreferenz vorangestellt. Das hat allerdings den Nachteil, dass eine zweite Referenz auf das Objekt erstellt wird, was beim Freigeben des Objekts zu Problemen führen kann. Doch dazu später mehr.
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Methoden - Konstruktor: __New():
Wie bereits oben gesagt, wird eine neue Instanz über das Schlüsselwort New erstellt (oder auch konstruiert). Im einfachsten Fall werden der Instanz dabei nur eine Referenz auf die Basisklasse übergeben und - so vorhanden - die in der Basisklasse vordefinierten Instanzvariablen erstellt. Wenn die Instanzvariablen für unterschiedliche Instanzen einer Basisklasse unterschiedliche Werte haben sollen, müsste man diese anschließend einzeln setzen, z.B. per
Das ist recht unbequem, und deshalb gibt es die reservierte 'Konstruktionsmethode' __New(). Die muss man in der Basisklasse definieren. Sie wird dann immer aufgerufen, wenn eine neue Instanz per Newabgeleitet wird. Normalerweise nutzt man das, um der neuen Instanz Parameter zu übergeben, deren Werte sich für unterschiedliche Instanzen unterscheiden. Die werden dann in Instanzvariablen abgelegt und können so innerhalb der Instanz genutzt werden. Die so erstellten Instanzvariablen müssen in der Basisklasse nicht vordefiniert werden, man kann es aber trotzdem tun. Die Parameter werden übergeben, indem man sie eingeschlossen In Klammern ( ... ) direkt an den dem Schlüsselwort New folgenden Klassennamen anhängt:
Ein schlichtes Beispiel:
Jede abgeleitete Mitarbeiterinstanz erlaubt so den Zugriff auf die für alle Mitarbeiter dieser Firma identischen Firmendaten über die Klassenvariablen und den Zugriff auf die persönlichen Daten des Mitarbeiters über die Instanzvariablen.
Man kann innerhalb des Konstruktors auch prüfen, ob die übergebenen Parameter logisch konsistent sind, z.B. einen bestimmten Aufbau haben oder innerhalb eines bestimmten Wertebereichs liegen. Stellt man Fehler fest, kann man das Erstellen einer neuen Instanz verhindern, indem man aus dem Konstruktor per Return Anweisung einen beliebigen Wert zurückgibt:
Wie bereits oben gesagt, wird eine neue Instanz über das Schlüsselwort New erstellt (oder auch konstruiert). Im einfachsten Fall werden der Instanz dabei nur eine Referenz auf die Basisklasse übergeben und - so vorhanden - die in der Basisklasse vordefinierten Instanzvariablen erstellt. Wenn die Instanzvariablen für unterschiedliche Instanzen einer Basisklasse unterschiedliche Werte haben sollen, müsste man diese anschließend einzeln setzen, z.B. per
Code: Select all
Instanz.InstanzVariable1 := "WertFürDieseInstanz"
Code: Select all
Instanz := New BasisKlasse(Param1, Param2, Param3)
Code: Select all
Class MitarbeiterBasisKlasse {
Static FirmenName := "Meine Firma"
Static FirmenStrasse := "Firmenstrasse 95"
Static FirmenOrt := "12000 Berlin"
__New(MitarbeiterName, MitarbeiterPersNR, MitarbeiterGehalt) {
This.MitarbeiterName := MitarbeiterName
This.MitarbeiterPersNR := MitarbeiterPersNR
This.MitarbeiterGehalt := MitarbeiterGehalt
}
}
MitarbeiterInstanz1 := New MitarbeiterBasisKlasse("Max Mustermann", "PN000001", "1200.55")
MitarbeiterInstanz2 := New MitarbeiterBasisKlasse("James Bond", "PN000007", "12000")
Man kann innerhalb des Konstruktors auch prüfen, ob die übergebenen Parameter logisch konsistent sind, z.B. einen bestimmten Aufbau haben oder innerhalb eines bestimmten Wertebereichs liegen. Stellt man Fehler fest, kann man das Erstellen einer neuen Instanz verhindern, indem man aus dem Konstruktor per Return Anweisung einen beliebigen Wert zurückgibt:
Code: Select all
Class BasisKlasse {
__New(Param1, Param2, Param3) {
Fehler := False
; ...
; Fehlerprüfung
; ...
If (Fehler)
Return 0 ; damit wird das erstellen der neuen Instanz verhindert
}
}
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Methoden - Destruktor: __Delete():
Der Destruktor wird dann aufgerufen, wenn man eine Instanz freigibt / zerstört / destruiert, wie z.B.Das wird allerdings nur in seltenen Fällen gebraucht. Die 'normalen' Speicherbereiche einer Instanz werden automatisch freigegeben. Ich beschränke mich deshalb hier darauf, dass die Methode mit dem dafür reservierten Namen __Delete() bei Bedarf ebenfalls in der Basisklasse definiert werden muss:
Im Gegensatz zum Konstruktor __New() können dem Destruktor __Delete() keine zusätzlichen Parameter übergeben werden.
Der Destruktor wird dann aufgerufen, wenn man eine Instanz freigibt / zerstört / destruiert, wie z.B.
Code: Select all
Instanz := ""
Code: Select all
Class BasisKlasse {
__Delete() {
MsgBox, %A_ThisFunc%
}
}
Instanz := New BasisKlasse
; ...
Instanz := "" ; gibt die Instanz frei und ruft die Methode __Delete() der Basisklasse auf
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Eigenschaften (Properties):
Die Eigenschaften werden im englischen Original "Properties" genannt. Sie definieren einen Namen für einen Wert, der abgefragt und/oder gesetzt werden kann, ohne dass der Wert in einer Klassen- oder Instanzvariablen gleichen Namens abgelegt wird. Dafür müssen innerhalb der Eigenschaftsdefinition die reservierten Methoden Get (Wert abfragen) und/oder Set (Wert setzen) definiert werden.
Die Dokumentation sagt dazu, dass sowohl beide als auch nur eine dieser Methoden definiert werden kann. Sie enthält dazu zwar einen Warnhinweis, ich möchte den hier aber noch einmal deutlich betonen:
Achtung: Wenn man die Methode Set nicht definiert, wird die Eigenschaft bei einer Wertzuweisung wie Klasse.Eigenschaft := "Bingo" in eine normale Instanzvariable mit dem zugewiesenen Wert umgewandelt. Das bedeutet, bei der nächsten Abfrage wird die Methode Get nicht mehr aufgerufen.
Jede Abfrage einer Eigenschaft wieruft die interne Methode Get, jede Wertzuweisung wie
ruft die interne Methode Set auf. Im Fall von Set wird der zugewiesene Wert in eine Variable mit dem reservierten Namen Value gestellt und ist so innerhalb von Get verfügbar.
Beim Abfragen und Zuweisen von Eigenschaftswerten können auch zusätzliche Parameter übergeben werden. Dafür hängt man eine Parameterliste eingeschlossen in eckige Klammern [...] direkt an den Eigenschaftsnamen an:Auf diese Parameter kann dann in Get und Set über die in der Parameterliste definierten Namen zugrgriffen werden. Die Zugriffe auf die Eigenschaft sehen dann so aus:
Wenn keine Parameter definiert werden, folgt dem Eigenschaftsnamen entweder ein leeres Klammerpaar [] oder auch 'gar nichts'. Die beiden folgenden Eigenschaftsdefinitionen sind deshalb gleichwertig:
Eigenschaften haben eine (für mich) entscheidende Einschränkung. Sie haben keine eigenen Bereiche, in denen Werte abgelegt werden können. Das bedeutet, die Methoden Get und Set können sich keine Variablen teilen, auf die beide zugreifen können. Wenn man in der Methode Set einen Wert speichern will, muss man deshalb dafür eine Instanzvariable der umgebenden Klasse / Instanz (normalerweise This) bemühen.
Beispiel:
Als Beispiel soll eine Klasse für RGB-Farbwerte herhalten. Sie übernimmt (speichert) einen kompletten RGB-Wert und stellt die einzelnen Farbanteile für Rot, Grün und Blau dynamisch über 'Eigenschaften' zur Verfüpgung, ohne dass diese Werte einzeln in der Klasse abgelegt werden. Über die Eigenschaften lassen sich die einzelnen Farbanteile auch setzen.
Die Eigenschaften werden im englischen Original "Properties" genannt. Sie definieren einen Namen für einen Wert, der abgefragt und/oder gesetzt werden kann, ohne dass der Wert in einer Klassen- oder Instanzvariablen gleichen Namens abgelegt wird. Dafür müssen innerhalb der Eigenschaftsdefinition die reservierten Methoden Get (Wert abfragen) und/oder Set (Wert setzen) definiert werden.
Code: Select all
Class MeineKlasse {
Eigenschaft[] { ; Name der Eigenschaft
Get { ; Wert abfragen
Return ...
}
Set { ; Wert setzen
...
Return ...
}
}
}
Achtung: Wenn man die Methode Set nicht definiert, wird die Eigenschaft bei einer Wertzuweisung wie Klasse.Eigenschaft := "Bingo" in eine normale Instanzvariable mit dem zugewiesenen Wert umgewandelt. Das bedeutet, bei der nächsten Abfrage wird die Methode Get nicht mehr aufgerufen.
Jede Abfrage einer Eigenschaft wie
Code: Select all
Wert := Instanz.Eigenschaft
Code: Select all
Instanz.Eigenschaft := "Wert"
Beim Abfragen und Zuweisen von Eigenschaftswerten können auch zusätzliche Parameter übergeben werden. Dafür hängt man eine Parameterliste eingeschlossen in eckige Klammern [...] direkt an den Eigenschaftsnamen an:
Code: Select all
Class MeineKlasse {
Eigenschaft[Param1, Param2, Param3] { ; Name der Eigenschaft und Parameterliste
Get { ; Wert abfragen
...
}
Set { ; Wert setzen
...
}
}
}
Code: Select all
Wert := Instanz.Eigenschaft[P1, P2, P3] ; Abfrage
; bzw
Instanz.Eigenschaft[P1, P2, P3] := "Wert" ; Zuweisung
Code: Select all
Class MeineKlasse {
Eigenschaft1[] { ; Name der Eigenschaft, keine Parameter
...
}
Eigenschaft2 { ; Name der Eigenschaft, keine Parameter
...
}
}
Beispiel:
Als Beispiel soll eine Klasse für RGB-Farbwerte herhalten. Sie übernimmt (speichert) einen kompletten RGB-Wert und stellt die einzelnen Farbanteile für Rot, Grün und Blau dynamisch über 'Eigenschaften' zur Verfüpgung, ohne dass diese Werte einzeln in der Klasse abgelegt werden. Über die Eigenschaften lassen sich die einzelnen Farbanteile auch setzen.
Code: Select all
Class RGB_Farbe {
Static Min := 0 ; kleinster RGB-Einzelwert
Static Max := 255 ; größter RGB-Einzelwert
; -----------------------------------------------------------------------------------------------------------------------------
__New(Farbe) {
If Farbe Is Not Integer ; wenn der übergebene Parameter keinen Integerwert enthält ...
Return 0 ; ... keine neue Instanz erstellen
This.Farbe := Farbe & 0xFFFFFF ; drei Bytes des Farbwerts (0xRRGGBB) in der Instanzvariablen 'Farbe' speichern
}
; -----------------------------------------------------------------------------------------------------------------------------
R[] { ; Eigenschaft 'R' - Rotanteil
Get {
Return (This.Farbe >> 16) & 0xFF
}
Set { ; der übergebene Wert wird in die reservierte Variable 'Value' gestellt.
If !This.IsValid(Value) ; wenn der übergebene Wert ungültig ist ...
Return This.R ; ... wird der unveränderte Rotanteil zurückgegeben
; Ansonsten werden der Wert der Instanzvariablen 'Farbe' angepasst ...
This.Farbe := (Value << 16) | (This.G << 8) | This.B
; ... und der übergebene Wert als Bestätigung zurückgegeben
Return Value
}
}
; -----------------------------------------------------------------------------------------------------------------------------
G[] { ; Eigenschaft 'G' - Grünanteil
Get {
Return (This.Farbe >> 8) & 0xFF
}
Set {
If !This.IsValid(Value)
Return This.G
This.Farbe := (This.R << 16) | (Value << 8) | This.B
Return Value
}
}
; -----------------------------------------------------------------------------------------------------------------------------
B[] { ; Eigenschaft 'B' - Blauanteil
Get {
Return (This.Farbe & 0xFF)
}
Set {
If !This.IsValid(Value)
Return This.B
This.Farbe := (This.R << 16) | (This.G << 8) | Value
Return Value
}
}
; -----------------------------------------------------------------------------------------------------------------------------
IsValid(FarbWert) { ; Methode zur Prüfung des Wertebereichs für einzelne Farbanteile
If Farbwert Is Not Integer
Return False
If (FarbWert < This.Min) || (FarbWert > This.Max)
Return False
Return True
}
}
MeineFarbe := New RGB_Farbe(0xF0C0A0)
MsgBox, 0, Farbe, % "Farbe: " . Format("{:06X}", MeineFarbe.Farbe) . "`n"
. "Rotanteil: " . MeineFarbe.R . "`n"
. "Grünanteil: " . MeineFarbe.G . "`n"
. "Blauanteil: " . MeineFarbe.B
MeineFarbe.G := 0x40
MsgBox, 0, Farbe, % "Farbe: " . Format("{:06X}", MeineFarbe.Farbe) . "`n"
. "Rotanteil: " . MeineFarbe.R . "`n"
. "Grünanteil: " . MeineFarbe.G . "`n"
. "Blauanteil: " . MeineFarbe.B
Last edited by just me on 20 Feb 2018, 06:04, edited 2 times in total.
Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)
Klassen - Subklassen:
Klassen können auch 'eingebettete' Subklassen enthalten:
Subklassen unterscheiden sich von 'normalen' Klassen nur in einer Hinsicht: Sie sind nicht super-glopbal. Das bedeutet, sie können nicht direkt über den Klassennamen angesprochen werden. Für den Zugriff auf Subklassen muss man ihrem Namen den Namen der übergeordneten Klasse bzw. Instanz voranstellen:
Sie sind damit ebenfalls 'eingekapselt'. Mehr gibt es hier dazu nicht zu sagen.
Klassen können auch 'eingebettete' Subklassen enthalten:
Code: Select all
Class MeineKlasse {
Class SubKlasse {
Static SubKlassenVar := "SubKlassenVar"
}
}
Code: Select all
MeineKlasse.SubKlasse.SubKlassenVar
; oder aus einer Klassenmethode heraus
This.SubKlasse.SubKlassenVar
Who is online
Users browsing this forum: No registered users and 39 guests