Code zur Programmiertechnik in Classic Visual Basic

Inhaltsverzeichnis

Im Normalfall wird man eher ein Array verwenden, die Klasse Collection sollte verwendet werden, wenn die speziellen Eigenschaften wie z. B. das Ansprechen eines Wertes über einen Schlüssel, benötigt werden.

Vielerorts hört man, daß die Verwendung von Collection-Objekten aus Gründen der Performanz nicht empfehlenswert ist. Der Grund dafür liegt im falschen Einsatz der Klasse Collection. Will man nämlich alle Elemente einer Collection durchlaufen, sollte dies nicht mit einer For…To-Schleife gemacht werden, die die Elemente nach ihren Indizes durchgeht, sondern über eine For…Each-Schleife.

Der Grund dafür ist, daß Visual Basic die Daten der Collection für den Indexbasierten Zugriff als lineare Liste speichert, wobei man immer nur vom Anfang der Liste zum nächsten Element gelangen kann. Jedes Listenelement besitzt einen Zeiger auf das nachfolgende Element, die Indizes sind nur virtuell vorhanden, indem, wenn z. B. das 10. Element in der Liste abgefragt wird, einfach 10 Mal vom ersten Element in der Liste weg immer zum Nachfolger des Elements in der Liste weitergeht.

Führt man das 100 Mal durch, läuft Visual Basic beim ersten Mal (für das Element mit Index 1) direkt zum ersten Element, beim zweiten Element wird wieder am Anfang der Liste (erstes Element) begonnen, dann zum zweiten Element weitergegangen, dabei ein Zähler inkrementiert und geprüft, ob er bereits gleich dem gesuchten Index ist. Anschließend wird dieses Element zurückgegeben. Dasselbe wird dann für alle weiteren Elemente durchgeführt, sodaß insgesamt zum Durchlaufen einer Liste mit n Elementen insgesamt die Summe von n=0 bis n Schritte erforderlich sind. Verwendet man nun eine For…Each-Schleife, wird nicht anhand der Indizes durchmustert, sondern direkt innerhalb der Liste nur zum nächsten Element (anhand des Zeigers) weitergegangen:

Dim c As Collection
Set c = New Collection

' Einige Elemente hinzufügen.
With c
    Call .Add("Computers")
    Call .Add("Modems")
    Call .Add("Soundcards")
End With

' Folgende Methode ist bedeutend langsamer als die unten angegebene.
Dim i As Integer
For i = 1 To c.Count
    Call DoSomeThing(c(i))
Next i

' Mit 'For...Each' ist das Durchlaufen bedeutend schneller.
Dim v As Variant
For Each v In c
    Call DoSomeThing(v)
Next v

Image- versus PictureBox-Steuerelement

Eine häufig gestellte Frage ist, was der Unterschied zwischen dem PictureBox- und Image-Steuerelement ist und wann man sich für welches der beiden Steuerelemente entscheiden soll. Das Image-Steuerelement benötigt weniger Speicher, allerdings stellt es weniger Funktionen zur Verfügung. Eine PictureBox muß verwendet werden, wenn mit Zeichenbefehlen in das Bild gezeichnet werden soll, ein Image-Steuerelement hingegen dann, wenn nur eine Grafik angezeigt werden soll. Interessant ist die Möglichkeit des Image-Steuerelements, die darin enthaltene Grafik zu strecken.

Grafikoperationen

Müssen viele Grafikoperationen hintereinander durchgeführt werden, ist es in einigen Fällen ratsam, auf die entsprechenden API-Funktionen zurückzugreifen. Will man jedoch mit Visual-Basic-eigenen Funktionen arbeiten, dann sollte man einige Hinweise beachten. Um ein Rechteck zu zeichnen, sollte die Line-Anweisung mit der Option B verwendet werden; das Zeichnen von vier einzelnen Linien ist viel aufwendiger, fehleranfälliger und auch langsamer. Um Punkte zu zeichnen, sollte die Line-Anweisung anstelle von PSet verwendet werden, da sie schneller ist.

Grafiken richtig verwenden

Visual Basic unterstützt mehrere Bildformate, die angezeigt werden können. Intern kennt Windows aber nur das Bitmap-Format. Wird eine Grafik geladen, muß sie zuerst evtl. dekomprimiert und anschließend in dieses Format transformiert werden. Das ist zeitaufwendig. Wenn es schnell gehen soll, sollten daher direkt Bitmaps anstelle von GIF- oder JPEG-Grafiken benutzt werden. Der Nachteil davon ist, daß die Dateien sehr groß sind. Muß man Speicherplatz sparen und ist dadurch auf komprimierte Formate angewiesen, sollte man nicht bei jeder Verwendung die Grafik neu laden, sondern sie zuerst in eine PictureBox einfügen und dann in alle Bereiche hineinkopieren, in denen man sie braucht.

Bewegen von Steuerelementen und Formularen

Um Steuerelemente oder Formulare zu verschieben, sollte deren Move-Methode verwendet werden. Das getrennte Setzen von Left und Top ist etwas langsamer.

Zugriff auf Eigenschaften von Steuerelementen

Benötigt man oft die Werte der Eigenschaften von Steuerelementen, ohne diese inzwischen zu verändern, dann sollte man diese in eine Variable zwischenspeichern, da das Ermitteln des Eigenschaftswertes länger dauert, als wenn man direkt den Wert aus einer Variablen verwendet.

Steuerelementfelder

Steuerelementfelder belegen weniger Speicher als einzelne Steuerelemente. Daher sollte man zumindest für Beschriftungen und statische Elemente der Benutzerschnittstelle Steuerelemente indizieren. Natürlich ist dies auch sinnvoll, wenn man auf Steuerelemente über den Index zugreifen will.

Die ClipControls-Eigenschaft

Diese Eigenschaft, z. B. bei Formularen zu finden, sollte immer auf den Wert False festgelegt sein. Die Eigenschaft gibt an, ob bei Aktualisierungen der Anzeige auch der Bereich unter den Steuerelementen aktualisiert werden soll. Meist besteht dazu aber kein Grund und es kann dadurch ein störendes Flackern verhindert werden, das ab und zu in Zusammenhang mit dieser Eigenschaft auftritt.

Anwendungsstart

Das Starten von Anwendungen sollte so schnell wie möglich ablaufen, im Normalfall sollte es nicht mehr als zwei Sekunden auf einem etwas älteren Rechner beanspruchen. Oft kann das aber nicht eingehalten werden und man muß auf Methoden zurückgreifen, die beim Benutzer den Eindruck erwecken, daß der Ladevorgang schneller vor sich geht.

Eine Möglichkeit dazu wäre, ein Willkommensformular anzuzeigen, auf dem Logo und Informationen zum Programm zu lesen sind. Bei besonders langen Ladevorgängen könnte hier auch der Status des Ladevorgangs integriert werden, entweder durch Angabe des gerade laufenden Operation oder über eine Fortschrittsanzeige. Bei sehr langen Ladezeiten (z. B., wenn eine Verbindung in das Internet oder zu einer Datenbank aufgebaut werden muß), könnte man dem Benutzer über eine kleine Animation, z. B. ein sich drehendes Rad, eine Rückmeldung geben, ob die Anwendung aktiv und nicht hängen geblieben ist. Eine andere Möglichkeit besteht darin, in der Form_Load-Prozedur vorzeitig ein Me.Show durchzuführen, um das Formular anzuzeigen, bevor der Ladevorgang durchgeführt wurde. Hier können jedoch Probleme auftreten, wenn der Benutzer Steuerelemente betätigt, die noch nicht initialisiert sind.

Die beiden genannten Maßnahmen ändern nichts an der tatsächlichen Zeit, es kann dadurch der Ladevorgang sogar noch langsamer ablaufen, doch es wird dem Benutzer suggeriert, daß die Anwendung bereits geladen ist oder daß ein Arbeitsprozeß durchgeführt wird – die wahrgenommene Geschwindigkeit steigt. Im Folgenden werden Methoden zur Beschleunigung angegeben, die dazu dienlich sind, die wirkliche Startzeit zu verringern. Formulare sollten nicht beim Start geladen werden, d. h., man soll nicht alle Formulare und Module gleich beim Anwendungsstart in den Speicher laden. Die Formulare können dann zwar schnell angezeigt werden, okkupieren aber schon von Anfang an Speicher, der sonst überhaupt nicht benötigt worden wäre.

Arbeitsspeicherbedarf

Beim Entwurf eines Programms sollte man darauf achten, daß nicht sinnlos Speicher belegt wird. Wenn nicht alle Programme, die aktiv sind, in den Arbeitsspeicher passen, werden Teile davon auf den Externspeicher, d. h., die Festplatte, ausgelagert. Diesen Vorgang nennt man Auslagerung, da dabei Speicherseiten ausgelagert und bei Bedarf wieder in den Hauptspeicher geladen werden. Durch diese Zugriffe kann die Laufzeit eines Programms in Mitleidenschaft gezogen werden. Es gibt eigene Werkzeuge, die Probleme in Bezug auf Arbeitsspeicherbelegung aufzeigen.

Toter Code

Unter totem Code versteht man Quellcode, der keine Funktion hat; z. B. eine Variable, die deklariert, allerdings nie verwendet wird. Dadurch wird Arbeitsspeicher sinnlos belegt bzw. reserviert. Mit speziellen Werkzeugen kann toter Code aufgespürt und entfernt werden, allerdings sollte man dafür sorgen, daß es nicht zu totem Code kommt. Toter Code ist nämlich ein Produkt iterativer Programmentwicklung. Iterative Programmentwicklung verfügt oft nicht über eine Analyse- und Entwurfsphase, sondern es wird ungeplant einfach „drauf los“ programmiert. Häufig sind dabei Änderungen des bisher erstellten Codes erforderlich, wodurch das Entstehen von totem Code erleichtert wird.

Inline-Code verwenden

Inline-Code bedeutet, daß Code einfach untereinander im „Hauptcode“ steht, und nicht in Prozeduren zusammengefaßt wird, die aufgerufen werden. Dadurch wird die Anwendung oft schneller, allerdings belegt sie mehr Speicher, da Code mehrfach vorhanden ist. Daher sollte man in zeitkritischen Anwendungen kleine Teile wie z. B. die Bestimmung eines Minimums von zwei Werten nicht in eine eigene Prozedur packen, sondern an jeder Stelle, an der die Funktion benötigt wird, den Code direkt angeben.

Zeilennummern

Zeilennummern stammen noch aus Vorläufern von Visual Basic. Damals konnten Zeilen anhand ihrer Nummer oder einer Bezeichnung angesprungen werden. Man kann Zeilennummern in Visual Basic 6.0 zwar noch immer verwenden, jedoch benötigen sie relativ viel Speicherplatz.

Module richtig verwenden

Visual Basic lädt Module „bei Bedarf“, d. h., wenn eine Funktion aus einem Modul benötigt wird, wird das gesamte Modul in den Speicher geladen. Aus diesem Grund ist es sinnvoll, mehrere kleine Module anstatt eines großen Moduls zu verwenden, nicht zuletzt deshalb, weil das Editieren des Quellcode dadurch erleichtert und die Übersichtlichkeit des Projekts gewahrt wird. Um die Anwendung schneller anzeigen zu lassen, sollten in der Form_Load-Prozedur des Startformulars keine Prozeduren aus anderen Modulen benutzt werden.

Debuginformationen

Debuginformationen sollten immer gelöscht und nicht mit der Anwendung ausgeliefert werden. Vor der Ausgabe des Programms sollte immer eine Releasekompilierung erfolgen. Debuginformation erleichtern es, Informationen über den Quellcode einer Anwendung zu erhalten, die der Endbenutzer eigentlich nicht bekommen sollte. Weiters wird unnötiger Arbeitsspeicher durch Debugcode in der Anwendung belegt.

Kompilieren zu P-Code oder Maschinencode?

Sehr rechenintensive Programme sollten zu Maschinencode kompiliert werden. Wenn Kompatibilität mit Debuggern und schnelle Ausführung gefragt ist, stellt Maschinencode die bessere Wahl dar. Jedoch sind Programme mit vielen Operationen auf Zeichenfolgen oder API-Aufrufen immer fast gleich schnell. P-Code wäre dann die bessere Lösung, da Programme dadurch eine geringere Dateigröße aufweisen. Bei Anwendungen, die über das Internet verbreitet werden, sollte wegen der Dateigröße P-Code der Vorzug gegeben werden.

Globale Eigenschaften

Es ist möglich, in einem normalen Modul öffentliche Eigenschaften zu definieren. Diese stellen eine interessante Alternative zu öffentlichen Variablen in Modulen dar, da zugewiesen Eigenschaftswerte auf Validität geprüft werden können. Außerdem können Eigenschaften vor Lesezugriff geschützt werden, was bei Variablen nicht möglich ist.

Daten in Modulen verstecken

Gibt es in einem Modul eine Variable auf Modulebene, die auch nach außen hin gelesen werden können muß, wird man sich meist mit damit behelfen, die Variable als öffentlich zu deklarieren. Damit ist die Variable aber nach außen hin sichtbar und nicht mehr gegen Änderung geschützt. Hier kann mittels einer öffentlichen Prozedur, die die private Variable retourniert oder aber einer schreibgeschützten Eigenschaft Abhilfe geschaffen werden. Im Folgenden wird der Code einer Funktionsprozedur und einer Eigenschaft zur Rückgabe des privaten Mitglieds angegeben:

Private m_NumberToHide As Integer

Public Function GetNumber() As Integer
    GetData = m_NumberToHide
End Function

Public Property Get Number() As Integer
    Number = m_NumberToHide
End Property