Diverser Code in Classic Visual Basic
Inhaltsverzeichnis
- Korrekte Verwendung der Funktion
Now - Kaufmännisches Runden von Zahlen
- Entfernen eines Eintrags aus einem Array
- Umwandeln von vorzeichenlosen in vorzeichenbehaftete Ganzzahlen
- Zurücksetzen von Objekten für ihre erneute Verwendung
- Vergleichen zweier Instanzen eines benutzerdefinierten Datentyps
Eine weitere Variante ist eine andere Schreibweise des vorigen Beispiels: Man verwendet dabei anstelle der numerischen Operationen bitweise exklusive Oder-Verknüpfungen. Dieses Verfahren ist in Visual Basic 6.0 sogar schneller als die Version mit den numerischen Operatoren.
Korrekte Verwendung der Funktion Now
Vorsicht mit dem Gebrauch der Funktion Now. Probieren Sie die folgenden beiden Beispiele:
' Liefert heutiges Datum vierstellig.
Ret1 = Format$(Now, "0000")
' Liefert heutiges Datum im Format TT.MM.JJJJ.
Ret2 = Format$(x, "DD.MM.YYYY")Das obige Beispiel funktioniert nur vor 12 Uhr mittags, sollte es später sein, so rundet Visual Basic auf den nächsten Tag auf, da die Funktion Now einen Wert des Typs Variant liefert, der in diesem Fall auf den nächsten Tag aufgerundet wird. Mit der Int-Anweisung kann Abhilfe geschaffen werden:
Ret1 = Format$(Int(Now), "0000")
Ret2 = Format$(x, "DD.MM.YYYY")Kaufmännisches Runden von Zahlen
Die Round-Funktion kann nicht kaufmännisch runden. Folgendes Beispiel würde 2 als Ergebnis liefern:
Call MsgBox(CStr(Round(2.5, 0)))Verwendet man jedoch die die folgende Funktion, erhält man den kaufmännisch gerundeten Wert 3. Bei negativen Zahlen wird ein eigentlich nicht korrektes Resultat zurückgegeben, der Leser kann sich aber leicht überlegen, wie der Code richtig aussehen müßte:
Private Function MRound( _
ByVal Number As Double, _
ByVal Decimals As Integer _
) As Double
MRound = Int(Number * 10 ^ Decimals + 0.5) / 10 ^ Decimals
End Function
Private Sub Main()
' Folgender Code rundet richtig auf n Nachkommastellen, hier 2.5 auf
' 0 Nachkommastellen.
Call MsgBox(CStr(MRound(2.5, 0)))
' Folgender Code rundet richtig auf positive Ganzzahlen, 2.5 ist die
' zu rundende Zahl.
Call MsgBox(CStr(Fix(2.5 + 0.5)))
End SubEntfernen eines Eintrags aus einem Array
Folgende Funktion kann eingesetzt werden, um ein Element aus einem Array zu entfernen. Es werden dabei alle nachfolgenden Elemente um einen Platz nach vor kopiert und das letzte Element entfernt. Diese Funktion hat aber im schlimmsten Fall lineare Laufzeit. Muß man aus einem Array Elemente entfernen, kann ggf. die Datenstruktur der linearen Liste bessere Resultate liefern, da hier die selbe Operation in konstanter Zeit durchgeführt werden kann, weil keine Elemente kopiert werden müssen:
Private Sub RemoveItemFromArray( _
ByRef Array As Variant, _
ByVal Index As Long _
)
If Index <= UBound(Array) And Index >= LBound(Array) Then
Dim i As Long
For i = Index To UBound(Array)
Array(Index) = Array(Index + 1)
Next i
ReDim Preserve Array(LBound(Array) To UBound(Array) - 1)
End If
End SubDer Aufruf könnte folgendermaßen erfolgen:
Dim c() As Integer ' Es muß sich um ein dynamisches Array handeln.
ReDim c(0 To 3) As Integer
c(0) = 2
c(1) = 22
c(2) = 23123
c(3) = 10
Call RemoveItemFromArray(c, 2)
Debug.Print c(2) ' Dieses Element hat den Wert 10.
Debug.Print c(3) ' Löst einen Fehler aus, da nicht mehr vorhanden.Umwandeln von vorzeichenlosen in vorzeichenbehaftete Ganzzahlen
Um einen vorzeichenlosen Integer (im Bereich von 0 bis 65.536) in einen Visual-Basic-Integer zu konvertieren, um ihn z. B. an API-Funktionen zu übergeben, kann folgende Funktion verwendet werden:
Private Sub Main()
Call MsgBox( _
"UInt" & vbTab & "Int" & vbNewLine & _
"0" & vbTab & UIntToInt(0) & vbNewLine & _
"422" & vbTab & UIntToInt(422) & vbNewLine & _
"21744" & vbTab & UIntToInt(21744) & vbNewLine & _
"32767" & vbTab & UIntToInt(32767) & vbNewLine & _
"43021" & vbTab & UIntToInt(43021) & vbNewLine & _
"65536" & vbTab & UIntToInt(65536) _
)
End Sub
Private Function UIntToInt(ByVal UInt As Long) As Integer
If UInt <= 32767 Then ' &H7FFF
UIntToInt = CInt(UInt)
Else
UIntToInt = CInt(UInt - 65536) ' &H10000
End If
End FunctionZurücksetzen von Objekten für ihre erneute Verwendung
Bei der Verwendung von eigenen Klassen in Schleifen kommt es oft vor, daß nach jedem Durchlauf die Eigenschaften des Objekts zurückgesetzt werden. Im schlimmsten Fall handelt es sich dabei um eine große Anzahl von Eigenschaften und Variablen, die entweder öffentlich zugänglich sind oder innerhalb der Klasse durch eigene Methoden auf bestimmte Startwerte gesetzt werden. In diesem Fall werden viele Programmierer auf eine bedeutend einfachere Methode zurückgreifen.
Nehmen wir an, wir müssen in einer Schleife 1.000 mal eine Klasse einsetzen, um bestimmte Operationen durchzuführen. Zu beachten ist, daß wir an Position (1) im folgenden Code immer erwarten, daß alle internen Variablen der Klasse zurückgesetzt sind. Anstatt eine aufwendige Reset-Prozedur zu implementieren, die auch noch bei Änderungen der Mitglieder der Klasse angepaßt werden muß, machen es sich viele Programmierer leichter und setzen nach jedem Schleifendurchlauf die Klasse auf das Schlüsselwort Nothing, wodurch der Speicher freigegeben wird und damit auch alle Variablen gelöscht werden. Anschließend wird am Anfang der Schleife der Variablen dann wieder eine neue Instanz der Klasse zugewiesen:
Dim i As Long
Dim MyParser As CParser
For i = 1 To 1000
Set MyParser = New CParser ' (1)
' Set properties here and call various class methods.
' Many properties and vars are set to other values.
Set MyParser = Nothing
Next iZusammenfassend kann gesagt werden, daß im obenstehenden Code immer für jeden Schleifendurchlauf Speicher (und das kann bei einer umfangreichen Klasse gar nicht so wenig sein) freigegeben und anschließend wieder neu belegt werden muß. So schnell Speicheroperationen auch sind, bei einer großen Anzahl von Durchläufen kann dies bereits sehr viel Zeit in Anspruch nehmen.
Wie bereits erwähnt, wäre es in den meisten Fällen effizienter, eine eigene Reset-Methode bzw. mehrere spezifische Reset-Methoden zu programmieren, in denen dann nur die entsprechenden Variablen zurückgesetzt werden. Dadurch entfällt die Freigabe des Speichers, das erneute Anfordern und das Setzen der Variablen auf die Anfangswerte. Der Code würde dann folgendermaßen aussehen:
Dim i As Long
Dim MyParser As CParser
Set MyParser = New CParser
For i = 1 To 1000
' Set properties here and call various class methods.
' Many properties and vars are set to other values.
Call MyParser.Reset
Next i
Set MyParser = NothingVergleichen zweier Instanzen eines benutzerdefinierten Datentyps
Variablen und Konstanten des gleichen Datentyps können mit dem Operator = auf Gleichheit getestet werden. Bei benutzerdefinierten Datentypen ist dies nicht so einfach möglich. Ein Ansatz, trotzdem zwei „Instanzen“ eines benutzerdefinierten Typs auf Gleichheit zu überprüfen besteht darin, die von den beiden Strukturen belegten Speicherbereiche in jeweils einer Zeichenfolge zu kopieren und diese dann zu vergleichen:
Private Declare Sub CopyMemory Lib "kernel32.dll" Alias "RtlMoveMemory" ( _
ByVal pDst As String, _
ByRef pSrc As Person, _
ByVal ByteLen As Long _
)
Private Type Person
Name As String * 100
Age As Byte
Size As Long
Male As Boolean
End Type
Private Sub Form_Load()
Dim p1 As Person
With p1
.Name = "Max Mustermann"
.Age = 20
.Size = 200
.Male = True
End With
Dim p2 As Person
With p2
.Name = "Max Mustermann"
.Age = 20
.Size = 200
.Male = True
End With
Call MsgBox(Equal(p1, p2)) ' Wahr.
p2.Name = "Donald Duck"
Call MsgBox(Equal(p1, p2)) ' Falsch.
End Sub
Private Function Equal(ByRef p1 As Person, ByRef p2 As Person) As Boolean
Dim Length As Long
Length = Len(p1)
Dim s1 As String, s2 As String
s1 = Space$(Length)
s2 = s1
Call CopyMemory(s1, p1, Length)
Call CopyMemory(s2, p2, Length)
Equal = (s1 = s2)
End Function