Types variants (Delphi)

De RAD Studio
Aller à : navigation, rechercher

Remonter à Types de données, variables et constantes - Index


Cette rubrique présente l'utilisation des types de données variants.

Présentation des variants

Il est parfois nécessaire de manipuler des données dont le type change ou est inconnu lors de la compilation. Dans ce cas, une des solutions consiste à utiliser des variables et des paramètres de type Variant qui représentent des valeurs dont le type peut changer à l'exécution. Les variants offrent une plus grande flexibilité que les variables standard mais consomment davantage de mémoire. De plus, les opérations où les variants sont utilisés sont plus lentes que celles portant sur des types associés statiquement. Enfin, les opérations illégales portant sur des variants provoquent fréquemment des erreurs d'exécution, alors que les mêmes erreurs avec des variables normales sont détectées à la compilation. Vous pouvez aussi créer des types de variants personnalisés.

Par défaut, les variants peuvent contenir des valeurs de tout type à l'exception des enregistrements, des ensembles, des tableaux statiques, des classes, des références de classes et des pointeurs. En d'autres termes, les variants peuvent tout contenir sauf les types structurés et les pointeurs. Ils peuvent contenir des interfaces, dont les méthodes et les propriétés sont accessibles grâce à eux. (Voir Interfaces d'objets.) Ils peuvent contenir des tableaux dynamiques et une forme particulière de tableaux statiques appelée tableau variant. Voir "Tableaux variants" plus loin dans cette rubrique. Il est possible de mélanger dans les expressions et les affectations des variants à des valeurs entières, réelles, chaînes ou Boolean : le compilateur effectue automatiquement les conversions des types.

Il n'est pas possible d'indexer les variants contenant des chaînes. Donc, si V est un variant contenant une valeur chaîne, la construction V[1] provoque une erreur d'exécution.

Vous pouvez définir des variants personnalisés qui étendent le type Variant afin de contenir des valeurs arbitraires. Par exemple, vous pouvez définir un type de chaîne variant qui permet l'indexation ou qui contient un tableau statique, un type enregistrement ou une référence de classe particulière. Les types variant personnalisés sont définis en créant des descendants de la classe TCustomVariantType.

Remarque : Ceci, comme la majorité des fonctionnalités des variants, est implémenté dans l'unité System.Variants.

Note: Les enregistrements variants sont considérés par nature comme étant non sûrs. Un enregistrement variant est très similaire à l'utilisation de la directive "absolute" car les parties champ variant de l'enregistrement se recouvrent littéralement en mémoire les unes sur les autres. Vous pouvez assigner une valeur dans un type, puis la lire sous un autre type. Si vous utilisez des variants, vous pouvez obtenir des avertissements du compilateur relatifs au code non protégé, tel que W1047 Code non protégé '%s' (Delphi).

Sur les plates-formes 32 bits, un variant est stocké sous la forme d'un enregistrement de 16 octets. Sur les plates-formes 64 bits, un variant est stocké sous la forme d'un enregistrement de 24 octets. un enregistrement variant est constitué d'un code de type et d'une valeur ou d'un pointeur sur une valeur ayant le type spécifié par le code du type. Tous les variants sont initialisés à la création avec une valeur spéciale Unassigned. La valeur spéciale Null indique des données inconnues ou manquantes.

La fonction standard VarType renvoie le code de type d'un variant. La constante varTypeMask est un masque de bits utilisé pour extraire le code de la valeur de retour de VarType. Par exemple

VarType(V) and varTypeMask = varDouble

renvoie True si V contient une valeur Double ou un tableau de Double. Le masque cache simplement le premier bit qui indique si le variant contient un tableau. Le type d'enregistrement TVarData défini dans l'unité System peut être utilisé pour transtyper des variants et accéder à leur représentation interne.

Conversions de types variants

Tous les types entiers, réels, chaîne, caractère et booléens sont compatibles pour l'affectation avec le type Variant. Les expressions peuvent être explicitement transtypées en variants. Les fonctions standard VarAsType et VarCast permettent de modifier la représentation interne d'un variant. Le code suivant illustre l'utilisation de variants et certaines conversions effectuées automatiquement quand des variants sont combinés avec d'autres types :

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;

Le compilateur effectue les conversions de types en respectant les règles suivantes :

Règles de conversion des types variants

      Cible :
Source :

integer

real

string

Boolean

integer

convertit au format entier

convertit en réel

convertit en représentation chaîne

renvoie False si la valeur est 0 et True sinon

real

arrondit à l'entier le plus proche

convertit au format réel

convertit en chaîne en utilisant les paramètres régionaux

renvoie False si la valeur est 0 et True sinon

string

convertit en entier en tronquant si nécessaire. Déclenche une exception si la chaîne n'est pas numérique.

convertit en réel en utilisant les paramètres régionaux. Déclenche une exception si la chaîne n'est pas numérique.

convertit au format chaîne/caractère

renvoie False si la chaîne est 'false' (insensible à la casse) ou une chaîne numérique qui s'évalue à 0, True si la chaîne est 'true' ou une chaîne numérique non nulle ; sinon déclenche une exception

character

pareil que pour une chaîne (ci-dessus)

pareil que pour une chaîne (ci-dessus)

pareil que pour une chaîne (ci-dessus)

pareil que pour une chaîne (ci-dessus)

Boolean

False = 0, True : tous les bits initialisés à 1 (-1 si type entier, 255 si octet, etc.)

False = 0, True = 1

False = 'False', True = 'True' par défaut ; la casse dépend de la variable globale System.Variants.BooleanToStringRule

False = False, True = True

Unassigned

renvoie 0

renvoie 0

renvoie une chaîne vide

renvoie False

Null

dépend de la variable globale System.Variants.NullStrictConvert (déclenche une exception par défaut)

dépend de la variable globale System.Variants.NullStrictConvert (déclenche une exception par défaut)

dépend des variables globales System.Variants.NullStrictConvert et System.Variants.NullAsStringValue (déclenche une exception par défaut)

dépend de la variable globale System.Variants.NullStrictConvert (déclenche une exception par défaut)


Les affectations hors de l'étendue provoquent fréquemment l'affectation à la variable cible de la plus grande valeur possible de son étendue. Les transtypages, affectations ou opérations sur les variants non valides déclenchent une exception Variants.EVariantError ou une classe d’exception dérivant de Variants.EVariantError.

Des règles de conversion spéciales s'appliquent au type System.TDateTime déclaré dans l'unité System. Quand un objet System.TDateTime est converti dans tout autre type, il est traité comme une valeur Double normale. Quand un entier, un réel ou un booléen est converti en un System.TDateTime, il est tout d'abord converti en Double puis lu comme une valeur date-heure. Quand une chaîne est convertie en un System.TDateTime, elle est interprétée comme une valeur date-heure en utilisant les paramètres régionaux. Quand une valeur non assignée est convertie en System.TDateTime, elle est traitée comme une valeur réelle ou entière nulle. La conversion de la valeur Null en System.TDateTime déclenche une exception.

Sur la plate-forme Win32, si un variant référence une interface COM, toute tentative de conversion lit la propriété par défaut de l'objet et convertit la valeur au type requis. Si l'objet n'a pas de propriété par défaut, une exception est déclenchée.

Variants dans les expressions

Tous les opérateurs, sauf ^, is et in, acceptent des opérandes variants. A l'exception des comparaisons, qui renvoient toujours un résultat Boolean, toute opération sur une valeur variant renvoie un résultat variant. Si une expression combine les variants avec des valeurs statiquement typées, ces valeurs sont converties automatiquement en variants.

Cela n'est pas vrai pour les comparaisons, où toute opération sur un variant Null produit un variant Null. Par exemple :

V := Null + 3;

assigne un variant Null à V. Par défaut, les comparaisons traitent le variant Null comme une valeur unique qui est inférieure à toute autre valeur. Par exemple :

if Null > -3 then ... else ...;

Dans cet exemple, la partie else de l'instruction if sera exécutée. Ce comportement peut être modifié en définissant les variables globales NullEqualityRule et NullMagnitudeRule.

Tableaux variants

Il n'est pas possible d'assigner un tableau statique ordinaire à un variant. Vous pouvez, à la place, créer un tableau variant en appelant l'une des fonctions standard VarArrayCreate ou VarArrayOf. Par exemple :

V: Variant;
   ...
V := VarArrayCreate([0,9], varInteger);

crée un tableau variant d'entiers de longueur 10 et l'assigne au variant V. Il est possible d'indexer le tableau en utilisant V[0], V[1], et ainsi de suite, mais il n'est pas possible de transmettre un élément de tableau variant en tant que paramètre var. Les tableaux variants sont toujours indexés avec des entiers.

Le deuxième paramètre dans l'appel de VarArrayCreate est le code du type de base du tableau. Pour obtenir la liste de ces codes, voir VarType. Ne transmettez jamais le code varString à VarArrayCreate ; pour créer un tableau variant de chaînes, utilisez varOleStr.

Les variants peuvent contenir des tableaux variants de différentes tailles, dimensions et types de base. Les éléments d'un tableau variant peuvent être de n'importe quel type admis dans les variants sauf ShortString et AnsiString. De plus, si le type de base du tableau est Variant, ses éléments peuvent même être hétérogènes. Utilisez la fonction VarArrayRedim pour redimensionner un tableau variant. Les autres routines standard agissant sur les tableaux variants sont VarArrayDimCount, VarArrayLowBound, VarArrayHighBound, VarArrayRef, VarArrayLock et VarArrayUnlock.

Remarque : Les tableaux variants de variants personnalisés ne sont pas supportés, puisque des instances de variants personnalisés peuvent être ajoutées à un tableau variant VarVariant.

Quand un variant contenant un tableau variant est affecté à un autre variant ou transmis en tant que paramètre par valeur, la totalité du tableau est copiée. Vous ne devez donc effectuer de telles opérations que si elles sont réellement nécessaires vu leur coût en mémoire.

OleVariant

La différence principale entre Variant et OleVariant est que Variant peut contenir des types de données que seule l'application en cours sait traiter. OleVariant contient uniquement des types de données compatibles avec OLE Automation, ce qui signifie que ces types de données peuvent être transférés entre programmes ou sur le réseau sans qu'il soit nécessaire de savoir si l'autre extrémité saura manipuler les données.

Quand vous assignez un Variant qui contient des données personnalisées (telles qu'une chaîne Delphi ou un des nouveaux types variants personnalisés) à un OleVariant, la bibliothèque d'exécution essaie de convertir le Variant en l'un des types de données OleVariant standard (une chaîne Delphi est convertie en chaîne OLE BSTR). Par exemple, si un variant contenant une chaîne AnsiString est assigné à un OleVariant, la chaîne AnsiString devient une chaîne WideString. La même chose est vraie lorsque vous passez un Variant à un paramètre de fonction OleVariant.

Voir aussi