Variante Typen (Delphi)
Nach oben zu Datentypen, Variablen und Konstanten - Index
In diesem Thema wird die Verwendung von varianten Typen erläutert.
Inhaltsverzeichnis
Allgemeines zu Varianten
Es gibt Situationen, in denen Daten verarbeitet werden müssen, deren Typ sich zur Laufzeit dynamisch ändert oder zur Compilierzeit noch nicht bekannt ist. In derartigen Fällen werden Variablen und Parameter des Typs Variant eingesetzt, die Werte repräsentieren, die ihren Typ zur Laufzeit ändern können. Varianten bieten eine große Flexibilität, brauchen aber mehr Arbeitsspeicherplatz als normale Variablen, und Operationen mit Varianten werden deutlich langsamer als mit statisch typisierten Werten ausgeführt. Außerdem führen unzulässige Operationen, die bei regulären Variablen während des Compilierens bereinigt werden, bei Varianten oft zu Laufzeitfehlern. Sie können auch benutzerdefinierte variante Typen erstellen.
Eine Variante kann zur Laufzeit die verschiedensten Typen annehmen. Records, Mengen, statische Arrays, Dateien, Klassen, Klassenreferenzen und Zeiger sind jedoch standardmäßig nicht erlaubt. Varianten können also Werte beliebigen Typs mit Ausnahme von strukturierten Typen und Zeigern enthalten. Wenn Varianten Interfaces enthalten, kann über diese auf ihre Methoden und Eigenschaften zugegriffen werden. (Weitere Informationen finden Sie unter Objekt-Interfaces.) Varianten können auch dynamische Arrays und variante Arrays (ein spezieller Typ von statischen Arrays) aufnehmen. (Siehe "Variante Arrays" weiter unten in diesem Kapitel.) Varianten können in Ausdrücken und Zuweisungen mit anderen Varianten, aber auch mit Integer-Werten, reellen Werten, Strings und booleschen Werten kombiniert werden. Die erforderlichen Typumwandlungen nimmt der Compiler automatisch vor.
Varianten, die Strings enthalten, können nicht indiziert werden. Wenn die Variante V beispielsweise einen String-Wert enthält, führt die Konstruktion V[1] zu einem Laufzeitfehler.
Sie können benutzerdefinierte Varianten definieren, wodurch der Typ Variant beliebige Werte enthalten kann. So lässt sich beispielsweise ein Varianten-String-Typ definieren, der die Indizierung ermöglicht oder der bestimmte Klassenreferenzen, Record-Typen oder statische Arrays enthält. Benutzerdefinierte Variantentypen werden definiert, indem Sie Nachkommen der Klasse TCustomVariantType erstellen.
Hinweis: Diese und fast die gesamte übrige Funktionalität von Varianten wird in der Unit System.Variants implementiert.
Hinweis: Variante Records werden als "unsicher" angesehen. Ein varianter Record entspricht weitestgehend der Verwendung der Direktive "absolute", weil die varianten Felder des Record im Arbeitsspeicher buchstäblich aufeinander gelegt werden. Sie können einen Wert als einen Typ zuweisen und ihn dann als einen anderen Typ auslesen. Bei der Verwendung von Varianten können Compiler-Warnungen über unsicheren Code, wie etwa W1047 Unsicherer Code '%s' (Delphi), erhalten.
Auf 32-Bit-Plattformen wird eine Variante als 16-Byte-Record gespeichert. Auf 64-Bit-Plattformen wird eine Variante als 24-Byte-Record gespeichert. Ein varianter Record besteht aus einem Typcode und einem Wert (bzw. einem Zeiger auf einen Wert), dessen Typ durch den Typcode festgelegt ist. Alle Varianten werden bei der Erstellung mit dem speziellen Wert Unassigned initialisiert. Der Wert Null steht für unbekannte oder fehlende Daten.
Der Typcode einer Variante kann mit der Standardfunktion VarType ermittelt werden. Die Konstante varTypeMask ist eine Bit-Maske, mit der der Typcode aus dem Rückgabewert der Funktion VarType isoliert werden kann. Der Ausdruck
VarType(V) and varTypeMask = varDouble
ergibt beispielsweise True, wenn V einen Double-Wert oder ein Array solcher Werte enthält. Die Maske verbirgt einfach das erste Bit, das anzeigt, ob die Variante ein Array enthält. Der in der Unit System definierte Record-Typ TVarData ermöglicht die Typumwandlung einer Variante, um Zugriff auf deren interne Darstellung zu erhalten.
Typkonvertierung bei Varianten
Alle Integer-Typen, reellen Typen, Strings, Zeichentypen und booleschen Typen sind zum Typ Variant zuweisungskompatibel. Ausdrücke können explizit in Varianten konvertiert werden. Zusätzlich kann die interne Darstellung einer Variante mit den Standardroutinen VarAsType und VarCast verändert werden. Das folgende Beispiel zeigt die Verwendung von Varianten und die automatische Konvertierung, die bei einer Kombination von Varianten mit anderen Typen durchgeführt wird:
var V1, V2, V3, V4, V5: Variant; I: Integer; D: Double; S: string; begin V1 := 1; { integer value } V2 := 1234.5678; { real value } V3 := 'Hello world!'; { string value } V4 := '1000'; { string value } V5 := V1 + V2 + V4; { real value 2235.5678} I := V1; { I = 1 (integer value) } D := V2; { D = 1234.5678 (real value) } S := V3; { S = 'Hello world!' (string value) } I := V4; { I = 1000 (integer value) } S := V5; { S = '2235.5678' (string value) } end;
Die folgende Tabelle enthält die Regeln, die bei der Konvertierung von Varianten in andere Typen gelten:
Regeln für die Typkonvertierung von Varianten
Ziel: |
integer |
real |
string |
Boolean |
integer |
Konvertiert Integer-Formate. |
Konvertiert in reelle Werte. |
Konvertiert in die String-Darstellung. |
Ergibt False, wenn 0, andernfalls True. |
real |
Rundet zum nächsten Integer. |
Konvertiert reelle Formate. |
Konvertiert in die String-Darstellung (verwendet länderspezifische Einstellungen). |
Ergibt False, wenn 0, andernfalls True. |
string |
Konvertiert in Integer (Wert wird evtl. abgeschnitten); wenn der String nicht numerisch ist, wird eine Exception ausgelöst. |
Konvertiert in reelle Werte (verwendet länderspezifische Einstellungen); wenn der String nicht numerisch ist, wird eine Exception ausgelöst. |
Konvertiert String-/Zeichenformate. |
Ergibt False, wenn der String 'false' (ohne Berücksichtigung der Groß-/Kleinschreibung) oder ein numerischer String ist, der zu 0 ausgewertet wird; ergibt True, wenn der String 'true' oder ein numerischer String ungleich 0 ist; andernfalls wird eine Exception ausgelöst. |
character |
Identisch mit String (oben). |
Identisch mit String (oben). |
Identisch mit String (oben). |
Identisch mit String (oben). |
Boolean |
False = 0, True: alle Bits werden auf 1 gesetzt (-1 wenn Integer, 255 wenn Byte, etc.) |
False = 0, True = 1 |
False = 'False', True = 'True' per Vorgabe; hängt von der globalen Variable System.Variants.BooleanToStringRule ab. |
False = False, True = True |
Unassigned |
Ergibt 0. |
Ergibt 0. |
Ergibt einen leeren String. |
Ergibt False. |
Null |
Hängt von der globalen Variable System.Variants.NullStrictConvert ab (löst per Vorgabe eine Exception aus). |
Hängt von der globalen Variable System.Variants.NullStrictConvert ab (löst per Vorgabe eine Exception aus). |
Hängt von den globalen Variablen System.Variants.NullStrictConvert und System.Variants.NullAsStringValue ab (löst per Vorgabe eine Exception aus). |
Hängt von der globalen Variable System.Variants.NullStrictConvert ab (löst per Vorgabe eine Exception aus). |
Wenn Zuweisungen außerhalb des zulässigen Bereichs liegen, wird der Zielvariable meist der höchste Wert im Bereich zugewiesen. Ungültige Variantenoperationen, Zuweisungen oder Umwandlungen führen zu einer <span class="codeInline">Variants.EVariantError</span>-Exception oder einer Exception-Klasse, die von <span class="codeInline">Variants.EVariantError</span> abgeleitet ist.
Für den in der Unit System deklarierten Typ System.TDateTime gelten gesonderte Konvertierungsregeln. System.TDateTime-Variablen werden bei einer Typumwandlung als normaler Double-Wert behandelt. Wenn ein Integer-Wert, ein reeller Wert oder ein boolescher Wert in den Typ System.TDateTime konvertiert wird, erfolgt zunächst eine Umwandlung in den Typ Double. Anschließend wird der Wert als Datums-/Zeitwert gelesen. Bei der Umwandlung eines Strings in den Typ System.TDateTime wird der String unter Verwendung der länderspezifischen Einstellungen als Datums-/Zeitwert interpretiert. Ein in den Typ System.TDateTime konvertierter Unassigned-Wert wird als reeller oder als Integer-Wert 0 behandelt. Bei der Konvertierung eines Null-Wertes in den Typ System.TDateTime wird eine Exception ausgelöst.
Für eine Variante, die in Win32 ein COM-Interface referenziert, wird beim Versuch einer Konvertierung die Standardeigenschaft des Objekts gelesen und der betreffende Wert in den geforderten Typ umgewandelt. Besitzt das Objekt keine Standardeigenschaft, wird eine Exception ausgelöst.
Varianten in Ausdrücken
Alle Operatoren außer ^, is und in unterstützen variante Operanden. Mit Ausnahme von Vergleichen, die stets zu einem booleschen Ergebnis führen, liefern alle Operationen mit varianten Werten ein variantes Ergebnis. Kombiniert ein Ausdruck Varianten mit Werten statischen Typs werden diese Werte automatisch in Varianten konvertiert.
Dies gilt nicht für Vergleiche, bei denen eine beliebige Operation an einer Null-Variante zu einer Null-Variante führt. Zum Beispiel:
V := Null + 3;
Hier wird V eine Null-Variante zugewiesen. Standardmäßig behandeln Vergleiche eine Null-Variante als eindeutigen Wert, der kleiner als jeder andere Wert ist. Zum Beispiel:
if Null > -3 then ... else ...;
In diesem Beispiel wird der else-Teil der if-Anweisung ausgeführt. Dieses Verhalten lässt sich durch Zuweisen der globalen Variablen NullEqualityRule und NullMagnitudeRule ändern.
Variante Arrays
Einer Variante kann kein normales statisches Array zugewiesen werden. Vielmehr wird ein variantes Array benötigt, das durch einen Aufruf der Standardfunktionen VarArrayCreate oder VarArrayOf erzeugt werden kann. Zum Beispiel:
V: Variant; ... V := VarArrayCreate([0,9], varInteger);
Diese Anweisung erzeugt ein variantes Array mit 10 Integer-Werten und weist es der Variante V zu. Das Array kann mit V[0], V[1] usw. indiziert werden. Es ist aber nicht möglich, Elemente eines varianten Arrays als var-Parameter zu übergeben. Variante Arrays werden immer mit Integer-Werten indiziert.
Der zweite Parameter in einem Aufruf von VarArrayCreate ist der Typcode für den Basistyp des Arrays. Eine Liste dieser Codes finden Sie unter VarType. Die Übergabe des Codes varString an VarArrayCreate ist nicht zulässig. Für variante String-Arrays muss der Typcode varOleStr verwendet werden.
Varianten können variante Arrays mit unterschiedlichen Größen, Dimensionen und Basistypen enthalten. Die Elemente eines varianten Arrays können jeden in Varianten zulässigen Typ mit Ausnahme von ShortString und AnsiString haben. Wenn das Array den Basistyp Variant hat, können die Elemente sogar heterogen sein. Die Größe eines varianten Arrays kann mit der Funktion VarArrayRedim verändert werden. Weitere Routinen für variante Arrays sind VarArrayDimCount, VarArrayLowBound, VarArrayHighBound, VarArrayRef, VarArrayLock und VarArrayUnlock.
Hinweis: Variante Arrays benutzerdefinierter Varianten werden nicht unterstützt, da Instanzen benutzerdefinierter Varianten dem varianten Array VarVariant hinzugefügt werden können.
Wenn eine Variante, die ein variantes Array enthält, einer anderen Variante zugewiesen oder als Wertparameter übergeben wird, entsteht eine Kopie des gesamten Arrays. Dieser Vorgang ist jedoch sehr speicherintensiv und sollte möglichst vermieden werden.
OleVariant
Der Hauptunterschied zwischen Variant und OleVariant besteht darin, dass Variant Datentypen enthalten kann, die nur der aktuellen Anwendung bekannt sind. OleVariant kann nur Datentypen enthalten, die zur OLE-Automatisierung kompatibel sind. Bei diesen Typen ist eine korrekte Verarbeitung der Daten sichergestellt, wenn sie programm- oder netzwerkübergreifend übergeben werden.
Wenn eine Variante mit benutzerdefinierten Daten (etwa einem Delphi-String oder einem der neuen benutzerdefinierten varianten Typen) einer OleVariant-Variable zugewiesen wird, versucht die Laufzeitbibliothek, die Variante in einen der Standarddatentypen von OleVariant umzuwandeln (z.B. einen Delphi-String in einen OLE-BSTR-String). Wenn beispielsweise eine Variante, die einen AnsiString enthält, einer OleVariant zugewiesen wird, wird der AnsiString zu einem WideString. Dasselbe gilt für die Übergabe einer Variante an einen OleVariant-Funktionsparameter.