VB5-VB6
Von: Ulrich Korndörfer [Home]
Bei einem dynamischen Array hat man nicht viele Möglichkeiten festzustellen, ob dieses bereits initialisiert wurde. Die Abfrage von UBound(m_Array) löst bereits einen Fehler aus, wenn ein uninitialisiertes Array übergeben wird. Diesen Fehler kann man natürlich abfangen und hat somit die Information, ob das Array initialisiert ist, oder nicht:
Dim lResult As Long On Error Resume Next lResult = UBound(m_Array) If CBool(Err.Number = 0) Then MsgBox "Array ist initialisiert" Else MsgBox "Array ist nicht initialisiert" End If
Es gibt aber noch einen anderen Weg, an diese Information zu gelangen - ohne einen Fehler auszulösen:
If Not (Not m_Array) Then MsgBox "Array initialisiert: "; LBound(m_Array); _ "-"; UBound(m_Array) Else MsgBox "Array nicht initialisiert" End If
Hier wird ein Fehler im Ausdrucksinterpreter von VB ausgenutzt. Der Ausdruck "Not m_Array" sollte eigentlich eine Fehlermeldung á lá "Typen unverträglich" provozieren, da hier versucht wird, einen skalaren Operator (Not) auf einen Vektor (m_Array) anzuwenden. Stattdessen wird fälschlicherweise m_Array als normale skalare Variable interpretiert, und deren Inhalt (das, was in der Speicheradresse steht, auf die m_Array verweist), an den Ausdrucksinterpreter zur Auswertung übergeben. Durch das doppelte Not ergibt der Ausdruck dann wieder den ursprünglichen Wert des Inhalts. Da der Inhalt von m_Array bei nicht initialisierten Arrays Null ist und bei initialisierten Arrays gleich der Adresse, an der die zum initialisierten Array gehörige Safearraystruktur steht, kann am Ergebnis des Ausdruckes abgelesen werden, ob das Array initialisiert ist (der Ausdruck ergibt einen Wert ungleich Null) oder nicht (der Wert des Ausdruckes ist Null). Durch die Verwendung des numerischen Ausdrucksergebnisses als Bedingung in einer If-Anweisung wird außerdem das numerische Ergebnis in einen boolschen Wert umgewandelt. Dabei gilt: Der numerische Wert 0 wird zu False, alles andere zu True umgewandelt.
Dass es sich hierbei wirklich um einen Fehler im Ausdrucksinterpreter (bzw. genauer um eine fehlerhafte Implementierung der Übersetzung eines Ausdruckes) handelt, kann man daran erkennen, dass alle anderen skalaren Rechenoperationen vom Compiler beim Kompilieren mit der Fehlermeldung "Typen unverträglich" abgeschmettert werden:
Dim x() As Long Dim y As Long y = Not x 'Wird fälschlicherweise nicht als fehlerhaft erkannt y = x Or 0 'Fehlermeldung y = -x 'Fehlermeldung y = (x) 'Fehlermeldung y = x + 1 'Fehlermeldung
Dabei spielt es keine Rolle, ob der Operator unär (wie das Minuszeichen als Vorzeichen) oder binär (wie z.B. der Or Operator) ist. In allen Fällen wird erkannt, dass fehlerhafter Weise skalare Operatoren auf einen Vektor angewendet werden sollen. Bis eben auf die eine Ausnahme: Not.
Es ist aber zu beachten, dass Not Not nur prüft, ob die Arrayvariable einen Wert enthält, der nicht Null ist. Es wird nicht geprüft, ob dies ein Pointer auf eine gültige SafeArrayStruktur ist. Allerdings sollte im Normalbetrieb nichts anderes der Fall sein.
Ebenfalls kann auf diese Weise nicht festgestellt werden, ob das Array ein "Zombie"-Array ist, wie es z.B. Split(""," ") und andere VB- Funktionen (bspw. Array()) zurückliefern. Bei Option Base 0 liefern Split und Array einen SAD mit LBound = 0 und UBound = -1. Bei Option Base 1 dagegen liefert Split LBound = 0 / UBound = -1, Array dagegen LBound = 1 und UBound = 0.
"Zombie"-Arrays treten häufiger auf. Wird korrekt unter Verwendung von UBound und LBound gearbeitet oder zusätzlich eine einfache Prüfung (If (UBound - LBound + 1) = 0 Then ...) verwendet, kann dieser Fall abgefangen werden.
Besonders wichtig ist, dass das Array nicht von einer Variablen vom Typ Variant gewrapped sein darf. Hier wird beim Kompilieren kein Fehler gemeldet, weil es, abhängig vom Inhalt der Variant-Variablen, völlig legal sein kann, auf diese Variable vom Typ Variant den Operator NOT anzuwenden. Da der aktuelle Inhalt der Variant-Variablen beim Kompilieren nicht bekannt ist, wird die Überprüfung darauf, ob das Anwenden des NOT-Operators erlaubt ist, später zur Laufzeit durch die zugrundeliegenden OLE-Variant-Funktionen durchgeführt. Und diese wiederum sind offensichtlich nicht fehlerhaft implementiert und melden zur Laufzeit den Fehler "Typen unverträglich", wenn die Variant-Variable ein Array enthält:
Dim l() As Long, Dim v As Variant On Error Resume Next v = l Print "l", Not Not l 'OK Print "v", Not Not v '"Typen unverträglich" Print "v bounds", LBound(v), UBound(v) '"Index außerhalb des gültigen Bereiches" ReDim l(0 To 1) v = l Print "l", Not Not l 'OK Print "v", Not Not v '"Typen unverträglich" Print "v bounds", LBound(v), UBound(v) 'OK
Speziell unter VB5, wo Arrays von Funktionen nur durch eine Variant zurück gegeben werden können, tritt der Fall Array in Variant häufiger auf. Aber auch in VB6 kommt dieser Fall sicher des öfteren vor (z.B. Arrays als Aktualparameter für ein ParamArray oder Arrays als Aktualparameter für einen Formalparameter vom Typ Variant).
Hingegen ist die Verwendung von Not Not bei einem Variant-Array OK:
Dim v() As Variant Print Not Not v 'OK, 0 v = Array() Print Not Not v 'OK, Wert ReDim v(0 To 1) Print Not Not v 'OK, gleicher Wert
Fazit
Der herkömmliche Weg, einen Fehler durch Abfragen von z.B. UBound zu provozieren, ist der Weg, der in allen Fällen funktioniert. Not Not ist eine schnelle und einfache Alternative, die aber nicht in allen Fällen anwendbar ist. Der Benutzer sollte also wissen, wann er dieses Konstrukt einsetzen darf.