Hallo zusammen,
ich möchte Euch hier eine erste Version von AutoItSimpleObjects vorstellen. Das Ziel ist es, erstes Feedback zu sammeln, um zu erfahren, wohin die Reise gehen soll - also was man sich als Community unter objektorientierung in AutoIt vorstellt, welche Funktionalität man sich dabei wünscht, und wie man damit einen Mehrwert für die Sprache bringen kann. Ich finde es schade, dass AutoItObject nicht mehr weiterentwickelt wird, und AutoItObject_Internal nur Attribute, setter()- und getter()- kann. Deswegen möchte ich das Thema aufgreifen. DISCLAIMER: Alles was ich im Folgenden über AutoItSimpleObjects sage, beschreibt den aktuellen Zustand - nicht immer unbedingt das finale Ziel. Wenn ich also z.B. sage, es gibt nur öffentliche Attribute - dann heißt das nicht, dass das so bleiben muss.
Wer noch nie von objektorientierter Programmierung gehört hat, tut sich gut daran, ggf. eine kurze Runde durch das Internet zu drehen. Starten kann man z.B. hier: https://de.wikipedia.org/wiki/Objektorientierung
1. Was kann AutoItSimpleObjects?* (*Stand jetzt)
AutoItSimpleObjects (kurz "ASO") ermöglicht die Erzeugung von einfachen Objekten, die Attribute und -Methoden besitzen können. Im Gegensatz zu AutoItObject werden dafür keine zusätzlichen Dateien wie z.B. DLL's benötigt.
Die Erzeugung von Objekten, Zuweisung von Methoden oder das Anlegen von Attributen ist dabei stark an das originale AutoItObject angelehnt - Alle Attribute und Methoden sind allerdings öffentlich.
AutoItSimpleObjects unterstützt 64-Bit und zur Not auch ByRef Parameter in Methodenaufrufen. Objekte können kopiert werden, was eine Art der Pseudo-Vererbung ermöglicht.
2. Was ist AutoItSimpleObjects?
Technisch gesehen ist AutoItSimpleObjects ein Wrapper zu AutoItObject_Internal, der mit einer eigenen Invoke-Methode und einigen wenigen Komfortfunktionen daherkommt.
AutoItObject beinhaltet somit die gesamte Funktionialität von AutoItObject_Internal und fügt ihr folgende wesentliche Merkmale hinzu:
- Die eigene Invoke-Methode ermöglicht den Aufruf von Methoden, die nicht __getter- oder __setter-Methoden sind.
- Alle Objekte haben eine zusätzliche Eigentschaft "__dirty".
Die Objekte, die in ASO erzeugt werden, sind Instanzen von IDispatch - Zugriff auf Attribute und Methodenaufrufe sind also Callbacks - deswegen sollten Methoden klein und kurz gehalten werden. (Bitte keine GUI-Messageloops in Methoden. :))
3. Was kann AutoItSimpleObjects (noch) NICHT?
- Vererbung
- Klassen
- Sichtbarkeiten
- Interfaces
4. Wie verwende ich AutoItSimpleObjects / Was gibt es zu beachten?
Im nachfolgenden ein kurzes Beispiel zur Anlage eines einfachen Objekts, der Zuweisung von Attributen und der Definition von Methoden:
AutoItSimpleObjects Example.au3
#include <AutoItSimpleObjects.au3>
#include <Array.au3>
#AutoIt3Wrapper_Run_Au3Check=y
$oObject = _ASO_Create()
_ASO_AddProperty($oObject, 'test')
_ASO_AddMethod($oObject, 'Method1', __method1)
_ASO_AddMethod($oObject, 'Method2_with_byref', _TestMethodByref)
_ASO_AddMethod($oObject, 'Method3', _method3)
; ------------------------------------------------------------------------------
; Test access to properties
; ------------------------------------------------------------------------------
$oObject.test = 12
MsgBox(0, "Access Property:", "The value of $oObject.test is: " & $oObject.test)
; ------------------------------------------------------------------------------
; Test access to Method (Method1, no ByRef)
; ------------------------------------------------------------------------------
$vRet = $oObject.Method1("Test Title", "Text")
MsgBox(0, "Access Method1:", 'Method1(...) returned: ' & @TAB & $vRet)
; ------------------------------------------------------------------------------
; Test access to Method (Method2, with ByRef)
; ------------------------------------------------------------------------------
Local $aArray[3] = [1, 2, 3]
_ArrayDisplay($aArray, 'ByRef Parameters: Array before')
; Attention: This does not work!
$oObject.Method2_with_byref($aArray, "Oh no!")
; The Workaround is:
$pMethod2_with_byref = $oObject.__get("Method2_with_byref")
$vRet = $pMethod2_with_byref($aArray, "New Value!")
_ArrayDisplay($aArray, 'ByRef Parameters: Array after')
MsgBox(0, "Access Method2:", 'Method2(...) returned: ' & @TAB & $vRet)
; ------------------------------------------------------------------------------
; Test access to Method (Method3, no parameters)
; ------------------------------------------------------------------------------
$vRet = $oObject.Method3()
MsgBox(0, "Access Method3:", 'Method3() returned: ' & @TAB & $vRet)
Func __method1($sTitle, $sParam)
Local $oThis = _ASO_This()
MsgBox(32, "Access Method1: " & $sTitle, _
StringFormat("Parameter $sParam is: \t\t%s\n" & _
"Object Property $oThis.test is: \t%s\n\n" & _
"Method will return: \t\t'42'", $sParam, $oThis.test))
Return 42
EndFunc ;==>__method1
Func _TestMethodByref(ByRef $aData, $sText)
Local $oThis = _ASO_This()
ReDim $aData[UBound($aData) + 1]
$aData[UBound($aData) - 1] = $sText
;_ArrayDisplay($aData, "inside method")
Return 'some or some other data'
EndFunc ;==>_TestMethodByref
Func _method3()
Local $oThis = _ASO_This()
MsgBox(0, "Access Method3:", "Hello from inside method3")
Return 1 + Random(1, 10000, 1)
EndFunc ;==>_method3
Alles anzeigen
Ansonsten kann AutoItSimpleObjects genau wie AutoItObject_Internal genutzt werden. Siehe dazu die Beispiele auf GitHub. Es gilt folgendes zu beachten:
- Im Gegensatz zu AutoItObject benötigen Funktionen keinen zusätzlichen Parameter $oSelf als ersten Parameter der Funktion. Die Deklaration von Methoden wird dadurch einfacher.
- Der Zugriff auf das aktuelle Objekt innerhalb einer Methode erfolgt stattdessen mittels Aufruf von $oThis = _ASO_This() am Anfang der Funktion.
- Der Zugriff auf eine Methode $oObject.Methodname() ohne Klammern (also $oObject.Methodname) ruft die zugewiesene Funktion ohne Parameter auf, statt eine Funktionsreferenz zurückzuliefern. Das ist technisch bedingt, weil uns AutoIt nicht ermöglicht, zwischen Attributszugriff und Methodenaufruf ohne Parameter zu unterscheiden. Zeile 48 könnte also auch so lauten: $vRet = $oObject.Method3**
- Die Nutzung von ByRef-Parametern in einem Methodenaufruf erfordert es, die Funktionsreferenz in einer separaten Variable zu speichern, siehe Zeile 30 ff. Dabei muss die Funktionsreferenz unbedingt mit $oObject.__get("Methodname") geholt werden - ein Zugriff mittels $pFunc = $oObject.Methodname führt zu einem Fehler. Siehe vorheriger Punkt.**
- Die Methoden _ASO_AddMethod(...) und _ASO_AddProperty(...) sind eigentlich überflüssig. Attribute müssen nicht explizit angelegt werden, sondern werden bei der ersten Zuweisung automatisch Public erstellt und Methoden werden dadurch erstellt, dass einem Attribute eine Funktionsreferenz zugewiesen wird. Zeile 6-11 im oberen Beispiel können also auch wie folgt lauten:
$oObject = _ASO_Create()
$oObject.test = ""
$oObject.Method1 = __method1
$oObject.Method2_with_byref = _TestMethodByref
$oObject.Method3 = _method3
** dieses Verhalten lässt sich durch das setzen des __dirty-Attributs eines Objektes auf 'True' ändern; ist ein Objekt __dirty, so wird es beim Zugriff auf eine Methode die Funktionsreferenz der zugewiesenen Funktion zurückliefern, statt sie ohne Parameter aufzurufen. Das hat zur Folge, dass Methoden ohne Parameter nur mit dem AutoIt Schlüsselwort "Call" aufgerufen werden können, oder die Funktionsreferenz in einer separaten Variable gespeichert werden muss, um sie ohne Parameter aufzurufen. Auf der anderen Seite können mit gesetztem __dirty-Flag ByRef-Parameter verwendet werden, ohne die Funktionsreferenz vorher explizit in einer Variable zu speichern.
Siehe dazu nachfolgendes Beispiel:
AutoItSimpleObjects Example_dirty.au3
Spoiler anzeigen
#include <AutoItSimpleObjects.au3>
#include <Array.au3>
#AutoIt3Wrapper_Run_Au3Check=n ; < Important for the ($oObject.Method)(...)-Syntax to work.
$oObject = _ASO_Create()
$oObject.__dirty = True
_ASO_AddProperty($oObject, 'test')
_ASO_AddMethod($oObject, 'Method1', __method1)
_ASO_AddMethod($oObject, 'Method2_with_byref', _TestMethodByref)
_ASO_AddMethod($oObject, 'Method3', _method3)
; ------------------------------------------------------------------------------
; Test access to properties
; ------------------------------------------------------------------------------
$oObject.test = 12
MsgBox(0, "Access Property:", "The value of $oObject.test is: " & $oObject.test)
; ------------------------------------------------------------------------------
; Test access to Method (Method1, no ByRef)
; ------------------------------------------------------------------------------
$vRet = $oObject.Method1("Test Title", "Text")
MsgBox(0, "Access Method1:", 'Method1(...) returned: ' & @TAB & $vRet)
; ------------------------------------------------------------------------------
; Test access to Method (Method2, with ByRef)
; ------------------------------------------------------------------------------
Local $aArray[3] = [1, 2, 3]
_ArrayDisplay($aArray, 'ByRef Parameters: Array before')
; Attention: This does not work!
$oObject.Method2_with_byref($aArray, "Oh no!")
; The Workaround is:
$vRet = ($oObject.Method2_with_byref) ($aArray, "New Value!")
_ArrayDisplay($aArray, 'ByRef Parameters: Array after')
MsgBox(0, "Access Method2:", 'Method2(...) returned: ' & @TAB & $vRet)
; ------------------------------------------------------------------------------
; Test access to Method (Method3, no parameters)
; ------------------------------------------------------------------------------
; Attention: This does not work!
$vRet = $oObject.Method3()
; The Workaround is:
$vRet = Call($oObject.Method3)
MsgBox(0, "Access Method3:", 'Method3() returned: ' & @TAB & $vRet)
Func __method1($sTitle, $sParam)
Local $oThis = _ASO_This()
MsgBox(32, "Access Method1: " & $sTitle, _
StringFormat("Parameter $sParam is:\t\t%s\n" & _
"Object Property $oThis.test is:\t%s\n\n" & _
"Method will return:\t\t'42'", $sParam, $oThis.test))
Return 42
EndFunc ;==>__method1
Func _TestMethodByref(ByRef $aData, $sText)
Local $oThis = _ASO_This()
ReDim $aData[UBound($aData) + 1]
$aData[UBound($aData) - 1] = $sText
;_ArrayDisplay($aData, "inside method")
Return 'some or some other data'
EndFunc ;==>_TestMethodByref
Func _method3()
Local $oThis = _ASO_This()
MsgBox(0, "Access Method3:", "Hello from inside method3")
Return 1 + Random(1, 10000, 1)
EndFunc ;==>_method3
Alles anzeigen
Zu beachten ist hier noch einmal folgende Kurzschreibweise, speziell für/mit ByRef-Paremetern.
In Zeile 1 ist ($oObject.Method2_with_byref) eingeklammert; deswege wird zuerst die Funktionsreferenz von Method2_with_byref aufgelöst. Danach wird die Funktion "normal" von AutoIt aufgerufen.
In Zeile 2 wird die Methode über das IDispatch-Interface aufgerufen, das keine ByRef Paramter unterstützt. FYI: Ohne gesetztes __dirty-Flag würde $oObject.Method2_with_byref in Zeile 1 über IDispatch ohne Parameter aufgerufen.
$vRet = ($oObject.Method2_with_byref)($aArray, "New Value!")
$vRet = $oObject.Method2_with_byref($aArray, "doesn't work :(")
5. Was brauche ich von Euch?
Anwendungsfälle, Ideen, Feedback.
Mal ins Blaue gedacht: Wofür würdet Ihr OO- benutzen? Sind Dinge wie Interfaces, Klassen, Traits, ByRef-Paramter, usw. für Euch unabdingbar bzw. was wäre Funktionalität, die Ihr nicht braucht?
Ist die UDF einfach genug - oder zu einfach gehalten? Sind die Syntaxmöglichkeiten in AutoIt ausreichend?
Die UDF: AutoItSimpleObjects.au3
siehe Post #2, oder Anhang
AutoItSimpleObjects Example.au3 / AutoItSimpleObjects Example_dirty.au3
s.o.