AHK Benutzerobjekte und Klassen - Grundbegriffe

Hilfreiche Erklärungen und Tipps zum Lernen von Autohotkey

Moderator: jNizM

just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

AHK Benutzerobjekte und Klassen - Grundbegriffe

27 Jan 2018, 05:27

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
Last edited by just me on 20 Feb 2018, 06:28, edited 5 times in total.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Begrifflichkeiten

27 Jan 2018, 05:51

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]
Last edited by just me on 11 Feb 2018, 04:57, edited 14 times in total.
Toralf n l i

Re: AHK Objekte und Klassen - Begrifflichkeiten

27 Jan 2018, 17:18

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.
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: AHK Objekte und Klassen - Begrifflichkeiten

28 Jan 2018, 03:36

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 :lolno:) doch so ist es dann für den gemeinen germanischen OOP novizen noch einfacher verdaulich.

Grazie mille :thumbup:
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: AHK Objekte und Klassen - Begrifflichkeiten

28 Jan 2018, 03:58

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
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Begrifflichkeiten (Baustelle)

28 Jan 2018, 04:10

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.
User avatar
nnnik
Posts: 4500
Joined: 30 Sep 2013, 01:01
Location: Germany

Re: AHK Objekte und Klassen - Begrifflichkeiten (Baustelle)

28 Jan 2018, 09:51

Die beiden Tutorials ergänzen sich ziemlich gut - diese Sorte von Grundwissen ist irgendwie Vorraussetzung bei meinem Tutorial.
Aber auf jeden Fall gute Arbeit.
Recommends AHK Studio
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:06

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:

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
}
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.

Code: Select all

ObjektName.VariablenName
; oder
ObjektName["VariablenName"]
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:
  • 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.
Alle AHK-Objekte - auch Klassen - bringen einen eingebauten Satz von Methoden mit, die in der Hilfe im Abschnitt Object aufgelistet sind. Man kann deshalb auch Klassen als Arrays 'missbrauchen'. Das soll das folgende (vorauseilende) Beispiel demonstrieren:

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|
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:
  • __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
Bis auf __New() und __Delete(), die 'relativ einfach' zu nutzen sind, sollte man recht genau wissen, was man tut, wenn man diese Methoden definiert. Ich werde mich zu den anderen hier nicht weiter äußern.

Achtung: Um einen möglichen Konflikt mit reservierten Methodennamen zu vermeiden, sollte man generell keine anderen Methoden definieren, deren Name mit zwei Unterstrichen beginnt.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:08

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:

Code: Select all

NeuesObjekt := New BasisObjekt
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:

Code: Select all

NeuesObjekt := {Base: BasisObjekt}
Hier wird ein neues Objekt erstellt, dem sein Basisobjekt über das Schlüsselwort Base untergeschoben wird.

Im Gegensatz dazu weist

Code: Select all

NeuesObjekt := BasisObjekt
der 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:

Code: Select all

Class MeineKlasse {
}
Instanz1 := New MeineKlasse
Instanz2 := New Instanz1
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.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:10

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:
  • Get - Abrufen eines Wertes.
  • Set - Setzen eines Wertes mit :=.
  • Call - Aufrufen einer Methode, gekennzeichnet durch ().
Diese internen Funktionen sorgen dafür, dass immer dann automatisch versucht wird, auf die ererbten Inhalte des Basisobjekts zuzugreifen, wenn ein Variablen- oder Methodenname in der Instanz nicht gefunden wird.

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
Wie aber auch bereits oben gesagt, sollte man aber erst einmal Grunderfahrungen im Umgang mit Objekten sammeln, bevor man diese Möglichkeiten nutzt.

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.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:12

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

Code: Select all

Objekt.Delete("MethodenName")
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.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:14

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:

Code: Select all

Class MeineKlasse {
}
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:

Code: Select all

Global MeineKlasse := {__Class: "MeineKlasse"}
Der eigentliche Sinn einer Klasse besteht aber darin, eigene Daten zu verwalten und auf diese über eigene Methoden zugreifen zu können.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:17

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.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:18

Klassen - Daten - Klassenvariablen:

Klassenvariablen werden mit dem Schlüsselwort Static gekennzeichnet.

Code: Select all

Class MeineKlasse {
   Static KlassenVar1 := "Wert1"
   Static KlassenVar2 := "Wert2"
}
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:

Code: Select all

Class MeineKlasse {
   Static KlassenVar1 := "Hallo"
   Static KlassenVar2 := MeineKlasse.KlassenVar1 . " Welt"
}
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.

Code: Select all

Wert := MeineKlasse.KlassenVar1
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.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:20

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:

Code: Select all

Class BasisKlasse {
   ; Klassenvariablen - mit Static
   Static KlassenVar1 := "Wert1"
   Static KlassenVar2 := "Wert2"
   ; Instanzvariablen - ohne Static
   InstanzVar1 := "Wert4"
   InstanzVar2 := "Wert5"
}
Wenn nun mit

Code: Select all

Instanz := New BasisKlasse
eine neue Instanz erstellt wird, verfügt die automatisch über 4 interne Variablen mit vorbelegten Werten :
  • 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.
Dynamisch erstellte Instanzvariablen:

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
Die 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.
Last edited by just me on 11 Feb 2018, 07:29, edited 1 time in total.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:22

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:

Code: Select all

Class MeineKlasse {
	Static KlassenVar := "KlassenVar"
	Methode() {
		;... Befehle
	}
}
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:

Code: Select all

MeineKlasse.Methode()
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:

Code: Select all

Global MeineKlasse := {KlassenVar: "KlassenVar", Methode: Func("Methode")}
Methode(Objektreferenz, Param1, Param2, ...) {
   ...
}
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:

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
	}
}
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.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:25

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

Code: Select all

Instanz.InstanzVariable1 := "WertFürDieseInstanz"
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:

Code: Select all

Instanz := New BasisKlasse(Param1, Param2, Param3)
Ein schlichtes Beispiel:

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")
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:

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
   }
}
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:27

Klassen - Methoden - Destruktor: __Delete():

Der Destruktor wird dann aufgerufen, wenn man eine Instanz freigibt / zerstört / destruiert, wie z.B.

Code: Select all

Instanz := ""
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:

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
Im Gegensatz zum Konstruktor __New() können dem Destruktor __Delete() keine zusätzlichen Parameter übergeben werden.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:29

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.

Code: Select all

Class MeineKlasse {
   Eigenschaft[] { ; Name der Eigenschaft
      Get { ; Wert abfragen
         Return ...
      }
      Set { ; Wert setzen
         ...
         Return ...
      }
   }
}
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 wie

Code: Select all

Wert := Instanz.Eigenschaft
ruft die interne Methode Get, jede Wertzuweisung wie

Code: Select all

Instanz.Eigenschaft := "Wert"
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:

Code: Select all

Class MeineKlasse {
   Eigenschaft[Param1, Param2, Param3] { ; Name der Eigenschaft und Parameterliste
      Get { ; Wert abfragen
         ...
      }
      Set { ; Wert setzen
         ...
      }
   }
}
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:

Code: Select all

Wert := Instanz.Eigenschaft[P1, P2, P3] ; Abfrage
; bzw
Instanz.Eigenschaft[P1, P2, P3] := "Wert" ; Zuweisung
Wenn keine Parameter definiert werden, folgt dem Eigenschaftsnamen entweder ein leeres Klammerpaar [] oder auch 'gar nichts'. Die beiden folgenden Eigenschaftsdefinitionen sind deshalb gleichwertig:

Code: Select all

Class MeineKlasse {
   Eigenschaft1[] { ; Name der Eigenschaft, keine Parameter
      ...
   }
   Eigenschaft2 { ; Name der Eigenschaft, keine Parameter
      ...
   }
}
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.

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.
just me
Posts: 9406
Joined: 02 Oct 2013, 08:51
Location: Germany

Re: AHK Objekte und Klassen - Grundbegriffe (Baustelle)

11 Feb 2018, 04:30

Klassen - Subklassen:

Klassen können auch 'eingebettete' Subklassen enthalten:

Code: Select all

Class MeineKlasse {
   Class SubKlasse {
      Static SubKlassenVar := "SubKlassenVar"
   }
}
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:

Code: Select all

MeineKlasse.SubKlasse.SubKlassenVar
; oder aus einer Klassenmethode heraus
This.SubKlasse.SubKlassenVar
Sie sind damit ebenfalls 'eingekapselt'. Mehr gibt es hier dazu nicht zu sagen.

Return to “Tutorials”

Who is online

Users browsing this forum: No registered users and 20 guests