Notes
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
La conversion est le processus de modification d’une valeur d’un type à un autre. Par exemple, une valeur de type Integer
peut être convertie en valeur de type, ou une valeur de type Derived
Double
peut être convertie en valeur de typeBase
, en supposant que Base
Derived
les deux classes et Derived
héritent de Base
. Les conversions peuvent ne pas nécessiter de modification de la valeur elle-même (comme dans le dernier exemple), ou elles peuvent nécessiter des modifications significatives dans la représentation de valeur (comme dans l’ancien exemple).
Les conversions peuvent être étendues ou étroites. Une conversion étendue est une conversion d’un type vers un autre type dont le domaine valeur est au moins aussi volumineux, s’il n’est pas plus grand, que le domaine de valeur du type d’origine. Les conversions d’élargissement ne doivent jamais échouer. Une conversion restrictive est une conversion d’un type vers un autre type dont le domaine de valeur est inférieur au domaine de valeur du type d’origine ou suffisamment non lié, ce qui doit être pris en cas de conversion (par exemple, lors de la conversion en Integer
String
). Les conversions restrictives, qui peuvent entraîner une perte d’informations, peuvent échouer.
La conversion d’identité (c’est-à-dire une conversion d’un type en lui-même) et la conversion de valeur par défaut (par exemple, une conversion à partir de Nothing
) sont définies pour tous les types.
Conversions implicites et explicites
Les conversions peuvent être implicites ou explicites. Les conversions implicites se produisent sans syntaxe spéciale. Voici un exemple de conversion implicite d’une Integer
Long
valeur en valeur :
Module Test
Sub Main()
Dim intValue As Integer = 123
Dim longValue As Long = intValue
Console.WriteLine(intValue & " = " & longValue)
End Sub
End Module
En revanche, les conversions explicites nécessitent des opérateurs de cast. La tentative d’effectuer une conversion explicite sur une valeur sans opérateur de cast provoque une erreur au moment de la compilation. L’exemple suivant utilise une conversion explicite pour convertir une Long
valeur en Integer
valeur.
Module Test
Sub Main()
Dim longValue As Long = 134
Dim intValue As Integer = CInt(longValue)
Console.WriteLine(longValue & " = " & intValue)
End Sub
End Module
L’ensemble de conversions implicites dépend de l’environnement de compilation et de l’instruction Option Strict
. Si la sémantique stricte est utilisée, seules les conversions étendues peuvent se produire implicitement. Si la sémantique permissive est utilisée, toutes les conversions étendues et restrictives (en d’autres termes, toutes les conversions) peuvent se produire implicitement.
Conversions booléennes
Bien qu’il Boolean
ne s’agit pas d’un type numérique, il a des conversions étroites vers et à partir des types numériques comme s’il s’agissait d’un type énuméré. Le littéral se convertit en littéral True
255
pour Byte
, 65535
pour UShort
, 4294967295
pour UInteger
, 18446744073709551615
pour ULong
, et en l’expression -1
pour SByte
, Short
, Integer
, , Long
, Decimal
, Single
et Double
. Le littéral False
se convertit en littéral 0
. Une valeur numérique zéro est convertie en littéral False
. Toutes les autres valeurs numériques sont converties en littéral True
.
Il existe une conversion étroite de booléen en chaîne, en conversion vers l’une ou l’autre System.Boolean.TrueString
.System.Boolean.FalseString
Il existe également une conversion restrictive à partir de String
: si la chaîne était égale ou TrueString
FalseString
(dans la culture actuelle, sans respect de la casse), elle utilise la valeur appropriée ; sinon, elle tente d’analyser la chaîne en tant que type numérique (dans hex ou octal si possible, sinon en tant que float) et utilise les règles ci-dessus ; sinon, elle lève System.InvalidCastException
.Boolean
Conversions numériques
Les conversions numériques existent entre les types Byte
, UShort
Integer
UInteger
SByte
Short
ULong
, Long
, Decimal
, Single
et Double
tous les types énumérés. En cas de conversion, les types énumérés sont traités comme s’ils étaient leurs types sous-jacents. Lors de la conversion en type énuméré, la valeur source n’est pas requise pour être conforme à l’ensemble de valeurs définies dans le type énuméré. Par exemple:
Enum Values
One
Two
Three
End Enum
Module Test
Sub Main()
Dim x As Integer = 5
' OK, even though there is no enumerated value for 5.
Dim y As Values = CType(x, Values)
End Sub
End Module
Les conversions numériques sont traitées au moment de l’exécution comme suit :
Pour une conversion d’un type numérique vers un type numérique plus large, la valeur est simplement convertie en type plus large. Les conversions de
UInteger
, ,Integer
ULong
,Long
ouDecimal
versSingle
ouDouble
sont arrondies à la valeur ouDouble
la plusSingle
proche. Bien que cette conversion puisse entraîner une perte de précision, elle ne provoquera jamais une perte de grandeur.Pour une conversion d’un type intégral vers un autre type intégral, ou de
Single
,Double
ouDecimal
vers un type intégral, le résultat dépend du fait que la vérification de dépassement d’entier soit activée :Si le dépassement de capacité entier est vérifié :
Si la source est un type intégral, la conversion réussit si l’argument source se trouve dans la plage du type de destination. La conversion lève une
System.OverflowException
exception si l’argument source est en dehors de la plage du type de destination.Si la source est
Single
,Double
ouDecimal
, la valeur source est arrondie vers le haut ou vers le bas à la valeur intégrale la plus proche, et cette valeur intégrale devient le résultat de la conversion. Si la valeur source est également proche de deux valeurs intégrales, la valeur est arrondie à la valeur qui a un nombre pair dans la position du chiffre le moins significatif. Si la valeur intégrale résultante est en dehors de la plage du type de destination, uneSystem.OverflowException
exception est levée.
Si le dépassement d’entier n’est pas vérifié :
Si la source est un type intégral, la conversion réussit toujours et consiste simplement à ignorer les bits les plus significatifs de la valeur source.
Si la source est
Single
,Double
ouDecimal
, la conversion réussit toujours et consiste simplement à arrondir la valeur source vers la valeur intégrale la plus proche. Si la valeur source est également proche de deux valeurs intégrales, la valeur est toujours arrondie à la valeur qui a un nombre pair dans la position du chiffre le moins significatif.
Pour une conversion de
Double
versSingle
, laDouble
valeur est arrondie à la valeur la plusSingle
proche. Si laDouble
valeur est trop petite pour représenter sous la forme d’unSingle
, le résultat devient zéro positif ou zéro négatif. Si laDouble
valeur est trop grande pour représenter en tant queSingle
, le résultat devient l’infini positif ou l’infini négatif. Si laDouble
valeur estNaN
, le résultat est égalementNaN
.Pour une conversion de
Single
ouDouble
versDecimal
, la valeur source est convertie enDecimal
représentation et arrondie au nombre le plus proche après la 28e décimale si nécessaire. Si la valeur source est trop petite pour représenter sous la forme d’unDecimal
, le résultat devient zéro. Si la valeur source estNaN
, l’infini ou trop grand pour représenter sous la forme d’unDecimal
, uneSystem.OverflowException
exception est levée.Pour une conversion de
Double
versSingle
, laDouble
valeur est arrondie à la valeur la plusSingle
proche. Si laDouble
valeur est trop petite pour représenter sous la forme d’unSingle
, le résultat devient zéro positif ou zéro négatif. Si laDouble
valeur est trop grande pour représenter en tant queSingle
, le résultat devient l’infini positif ou l’infini négatif. Si laDouble
valeur estNaN
, le résultat est égalementNaN
.
Conversions de référence
Les types de référence peuvent être convertis en type de base, et inversement. Les conversions d’un type de base vers un type plus dérivé réussissent uniquement au moment de l’exécution si la valeur convertie est une valeur Null, le type dérivé lui-même ou un type plus dérivé.
Les types de classes et d’interface peuvent être castés vers et à partir de n’importe quel type d’interface. Les conversions entre un type et un type d’interface réussissent uniquement au moment de l’exécution si les types réels impliqués ont une relation d’héritage ou d’implémentation. Étant donné qu’un type d’interface contient toujours une instance d’un type qui dérive de Object
, un type d’interface peut également toujours être casté vers et depuis Object
.
Remarque. Il n’est pas une erreur de conversion d’une NotInheritable
classe vers et à partir d’interfaces qu’elle n’implémente pas, car les classes qui représentent des classes COM peuvent avoir des implémentations d’interface qui ne sont pas connues jusqu’au moment de l’exécution.
Si une conversion de référence échoue au moment de l’exécution, une System.InvalidCastException
exception est levée.
Conversions de variance de référence
Les interfaces ou délégués génériques peuvent avoir des paramètres de type variant qui autorisent les conversions entre les variantes compatibles du type. Par conséquent, lors de l’exécution, une conversion d’un type de classe ou d’un type d’interface vers un type d’interface compatible avec un type d’interface qu’il hérite ou implémente réussit. De même, les types délégués peuvent être castés vers et à partir de types délégués compatibles variants. Par exemple, le type de délégué
Delegate Function F(Of In A, Out R)(a As A) As R
autoriserait une conversion de F(Of Object, Integer)
vers F(Of String, Integer)
. Autrement dit, un délégué F
qui prend Object
peut être utilisé en toute sécurité comme délégué F
qui accepte String
. Lorsque le délégué est appelé, la méthode cible attend un objet et une chaîne est un objet.
Un délégué générique ou un type S(Of S1,...,Sn)
d’interface est dit être compatible avec une interface générique ou un type T(Of T1,...,Tn)
délégué si :
S
etT
sont tous deux construits à partir du même typeU(Of U1,...,Un)
générique.Pour chaque paramètre
Ux
de type :Si le paramètre de type a été déclaré sans variance,
Sx
etTx
doit être le même type.Si le paramètre de type a été déclaré
In
, il doit y avoir une identité étendue, une valeur par défaut, une référence, un tableau ou une conversion de paramètre de type àTx
partir deSx
.Si le paramètre de type a été déclaré
Out
, il doit y avoir une identité étendue, une valeur par défaut, une référence, un tableau ou une conversion de paramètre de type àSx
partir deTx
.
Lors de la conversion d’une classe en interface générique avec des paramètres de type variant, si la classe implémente plusieurs interfaces compatibles variant, la conversion est ambiguë s’il n’existe pas de conversion non variant. Par exemple:
Class Base
End Class
Class Derived1
Inherits Base
End Class
Class Derived2
Inherits Base
End Class
Class OneAndTwo
Implements IEnumerable(Of Derived1)
Implements IEnumerable(Of Derived2)
End Class
Class BaseAndOneAndTwo
Implements IEnumerable(Of Base)
Implements IEnumerable(Of Derived1)
Implements IEnumerable(Of Derived2)
End Class
Module Test
Sub Main()
' Error: conversion is ambiguous
Dim x As IEnumerable(Of Base) = New OneAndTwo()
' OK, will pick up the direct implementation of IEnumerable(Of Base)
Dim y as IEnumerable(Of Base) = New BaseAndOneAndTwo()
End Sub
End Module
Conversions de délégués anonymes
Lorsqu’une expression classifiée comme méthode lambda est reclassée en tant que valeur dans un contexte où il n’existe aucun type cible (par exemple), Dim x = Function(a As Integer, b As Integer) a + b
ou où le type cible n’est pas un type délégué, le type de l’expression résultante est un type délégué anonyme équivalent à la signature de la méthode lambda. Ce type de délégué anonyme a une conversion vers n’importe quel type délégué compatible : un type délégué compatible est n’importe quel type délégué qui peut être créé à l’aide d’une expression de création de délégué avec la méthode du Invoke
type délégué anonyme en tant que paramètre. Par exemple:
' Anonymous delegate type similar to Func(Of Object, Object, Object)
Dim x = Function(x, y) x + y
' OK because delegate type is compatible
Dim y As Func(Of Integer, Integer, Integer) = x
Notez que les types System.Delegate
et System.MulticastDelegate
ne sont pas eux-mêmes considérés comme des types délégués (même si tous les types délégués héritent d’eux). Notez également que la conversion du type délégué anonyme vers un type délégué compatible n’est pas une conversion de référence.
Conversions de tableau
Outre les conversions définies sur les tableaux en raison du fait qu’elles sont des types de référence, plusieurs conversions spéciales existent pour les tableaux.
Pour deux types A
et B
, s’ils sont tous deux des types de référence ou des paramètres de type non connus comme des types valeur, et s’il A
a une conversion de référence, de tableau ou de paramètre de type en B
, une conversion existe d’un tableau de type A
en tableau de type B
avec le même rang. Cette relation est appelée covariance de tableau. La covariance de tableau signifie en particulier qu’un élément d’un tableau dont le type d’élément est B
peut être en fait un élément d’un tableau dont le type d’élément est A
, à condition que les deux A
types de référence et B
qui B
possèdent une conversion de référence ou une conversion de tableau en A
. Dans l’exemple suivant, le deuxième appel provoque F
la levée d’une System.ArrayTypeMismatchException
exception, car le type d’élément réel est String
b
, et non Object
:
Module Test
Sub F(ByRef x As Object)
End Sub
Sub Main()
Dim a(10) As Object
Dim b() As Object = New String(10) {}
F(a(0)) ' OK.
F(b(1)) ' Not allowed: System.ArrayTypeMismatchException.
End Sub
End Module
En raison de la covariance de tableau, les affectations aux éléments des tableaux de type référence incluent une vérification au moment de l’exécution qui garantit que la valeur affectée à l’élément de tableau est en fait d’un type autorisé.
Module Test
Sub Fill(array() As Object, index As Integer, count As Integer, _
value As Object)
Dim i As Integer
For i = index To (index + count) - 1
array(i) = value
Next i
End Sub
Sub Main()
Dim strings(100) As String
Fill(strings, 0, 101, "Undefined")
Fill(strings, 0, 10, Nothing)
Fill(strings, 91, 10, 0)
End Sub
End Module
Dans cet exemple, l’affectation à array(i)
dans la méthode Fill
inclut implicitement une vérification au moment de l’exécution qui garantit que l’objet référencé par la variable value
est soit Nothing
une instance d’un type compatible avec le type d’élément réel du tableau array
. Dans la méthode Main
, les deux premiers appels de méthode Fill
réussissent, mais le troisième appel entraîne la levée d’une System.ArrayTypeMismatchException
exception lors de l’exécution de la première affectation sur array(i)
. L’exception se produit parce qu’une Integer
exception ne peut pas être stockée dans un String
tableau.
Si l’un des types d’éléments de tableau est un paramètre de type dont le type s’avère être un type valeur au moment de l’exécution, une System.InvalidCastException
exception est levée. Par exemple:
Module Test
Sub F(Of T As U, U)(x() As T)
Dim y() As U = x
End Sub
Sub Main()
' F will throw an exception because Integer() cannot be
' converted to Object()
F(New Integer() { 1, 2, 3 })
End Sub
End Module
Les conversions existent également entre un tableau d’un type énuméré et un tableau du type sous-jacent du type énuméré ou un tableau d’un autre type énuméré avec le même type sous-jacent, à condition que les tableaux aient le même rang.
Enum Color As Byte
Red
Green
Blue
End Enum
Module Test
Sub Main()
Dim a(10) As Color
Dim b() As Integer
Dim c() As Byte
b = a ' Error: Integer is not the underlying type of Color
c = a ' OK
a = c ' OK
End Sub
End Module
Dans cet exemple, un tableau de Color
données est converti en et à partir d’un tableau de type sous-jacent Color
.Byte
La conversion en tableau de Integer
, toutefois, est une erreur, car Integer
n’est pas le type sous-jacent de Color
.
Un tableau de type A()
rank-1 a également une conversion de tableau en types IList(Of B)
d’interface de collection , IReadOnlyList(Of B)
, ICollection(Of B)
IReadOnlyCollection(Of B)
et IEnumerable(Of B)
trouvé dans System.Collections.Generic
, tant que l’un des éléments suivants est vrai :
-
A
etB
sont à la fois des types de référence ou des paramètres de type non connus comme des types valeur ; etA
ont une conversion de référence, de tableau ou de paramètre de type étendue enB
; ou -
A
etB
sont tous deux des types énumérés du même type sous-jacent ; ou - l’un des
A
types énumérés etB
l’autre est son type sous-jacent.
Tout tableau de type A avec n’importe quel rang a également une conversion de tableau en types IList
d’interface de collection non génériques , ICollection
et IEnumerable
trouvé dans System.Collections
.
Il est possible d’effectuer une itération sur les interfaces résultantes à l’aide For Each
de , ou d’appeler directement les GetEnumerator
méthodes. Dans le cas des tableaux de rang 1 convertis sous forme générique ou non générique ou IList
ICollection
, il est également possible d’obtenir des éléments par index. Dans le cas des tableaux de rang 1 convertis en formes génériques ou non génériques de , il est également possible de IList
définir des éléments par index, soumis aux mêmes vérifications de covariance du tableau d’exécution, comme décrit ci-dessus. Le comportement de toutes les autres méthodes d’interface n’est pas défini par la spécification du langage VB ; il est jusqu’au runtime sous-jacent.
Conversions de type valeur
Une valeur de type valeur peut être convertie en un de ses types de référence de base ou un type d’interface qu’il implémente via un processus appelé boxing. Lorsqu’une valeur de type valeur est boxée, la valeur est copiée à partir de l’emplacement où elle réside sur le tas .NET Framework. Une référence à cet emplacement sur le tas est ensuite retournée et peut être stockée dans une variable de type référence. Cette référence est également appelée instance boxed du type valeur. L’instance boxed a la même sémantique qu’un type référence au lieu d’un type valeur.
Les types de valeurs boxed peuvent être convertis en leur type valeur d’origine par le biais d’un processus appelé unboxing. Lorsqu’un type de valeur boxed est unboxed, la valeur est copiée à partir du tas dans un emplacement de variable. À partir de ce stade, il se comporte comme s’il s’agissait d’un type valeur. Lorsque vous supprimez une zone d’un type valeur, la valeur doit être une valeur Null ou une instance du type valeur. Sinon, une System.InvalidCastException
exception est levée. Si la valeur est une instance d’un type énuméré, cette valeur peut également être unboxed vers le type sous-jacent du type énuméré ou un autre type énuméré qui a le même type sous-jacent. Une valeur Null est traitée comme s’il s’agissait du littéral Nothing
.
Pour prendre en charge correctement les types valeur nullables, le type System.Nullable(Of T)
valeur est traité spécialement lors de la boxe et de l’annulation de la réception. La boxe d’une valeur de type Nullable(Of T)
entraîne une valeur boxée de type T
si la propriété de HasValue
la valeur est True
ou une valeur de Nothing
si la propriété de HasValue
la valeur est False
. Annuler la boîte de réception d’une valeur de type T
pour Nullable(Of T)
obtenir une instance de Nullable(Of T)
dont Value
la propriété est la valeur boxed et dont HasValue
la propriété est True
. La valeur Nothing
peut être unboxed pour Nullable(Of T)
n’importe quelle T
valeur et génère une valeur dont HasValue
la propriété est False
. Étant donné que les types valeur boxed se comportent comme des types de référence, il est possible de créer plusieurs références à la même valeur. Pour les types primitifs et les types énumérés, cela n’est pas pertinent, car les instances de ces types sont immuables. Autrement dit, il n’est pas possible de modifier une instance boxed de ces types. Il n’est donc pas possible d’observer le fait qu’il existe plusieurs références à la même valeur.
Les structures, en revanche, peuvent être mutables si ses variables d’instance sont accessibles ou si ses méthodes ou propriétés modifient ses variables d’instance. Si une référence à une structure boxée est utilisée pour modifier la structure, toutes les références à la structure boxée voient la modification. Étant donné que ce résultat peut être inattendu, lorsqu’une valeur tapée telle qu’elle Object
est copiée d’un emplacement vers un autre type de valeur boxé est automatiquement clonée sur le tas au lieu d’avoir simplement leurs références copiées. Par exemple:
Class Class1
Public Value As Integer = 0
End Class
Structure Struct1
Public Value As Integer
End Structure
Module Test
Sub Main()
Dim val1 As Object = New Struct1()
Dim val2 As Object = val1
val2.Value = 123
Dim ref1 As Object = New Class1()
Dim ref2 As Object = ref1
ref2.Value = 123
Console.WriteLine("Values: " & val1.Value & ", " & val2.Value)
Console.WriteLine("Refs: " & ref1.Value & ", " & ref2.Value)
End Sub
End Module
La sortie du programme est la suivante :
Values: 0, 123
Refs: 123, 123
L’affectation au champ de la variable val2
locale n’affecte pas le champ de la variable val1
locale, car lorsque le boxed Struct1
a été affecté à val2
, une copie de la valeur a été effectuée. En revanche, l’affectation ref2.Value = 123
affecte l’objet à la fois ref1
et ref2
référence.
Remarque. La copie de structure n’est pas effectuée pour les structures boxed typées comme étant donné qu’il System.ValueType
n’est pas possible de System.ValueType
désactiver la liaison tardive.
Il existe une exception à la règle selon laquelle les types de valeurs boxés seront copiés lors de l’affectation. Si une référence de type valeur boxed est stockée dans un autre type, la référence interne ne sera pas copiée. Par exemple:
Structure Struct1
Public Value As Object
End Structure
Module Test
Sub Main()
Dim val1 As Struct1
Dim val2 As Struct1
val1.Value = New Struct1()
val1.Value.Value = 10
val2 = val1
val2.Value.Value = 123
Console.WriteLine("Values: " & val1.Value.Value & ", " & _
val2.Value.Value)
End Sub
End Module
La sortie du programme est la suivante :
Values: 123, 123
Cela est dû au fait que la valeur boxée interne n’est pas copiée lorsque la valeur est copiée. Ainsi, les deux val1.Value
et val2.Value
ont une référence au même type de valeur boxed.
Remarque. Le fait que les types valeur boxed interne ne sont pas copiés est une limitation du système de type .NET - pour s’assurer que tous les types de valeurs boxed internes ont été copiés chaque fois qu’une valeur de type Object
a été copiée serait prohibitivement coûteuse.
Comme décrit précédemment, les types de valeurs boxed ne peuvent être non boxés qu’à leur type d’origine. Toutefois, les types primitifs boxés sont traités spécialement lorsqu’ils sont typés comme Object
. Ils peuvent être convertis en n’importe quel autre type primitif vers lequel ils ont une conversion. Par exemple:
Module Test
Sub Main()
Dim o As Object = 5
Dim b As Byte = CByte(o) ' Legal
Console.WriteLine(b) ' Prints 5
End Sub
End Module
Normalement, la valeur 5
boxed n’a pas pu être décochée Integer
dans une Byte
variable. Toutefois, étant donné que Integer
les Byte
types primitifs et ont une conversion, la conversion est autorisée.
Il est important de noter que la conversion d’un type valeur en interface est différente d’un argument générique limité à une interface. Lors de l’accès aux membres d’interface sur un paramètre de type limité (ou les méthodes d’appel sur Object
), la boxe ne se produit pas, car elle se produit lorsqu’un type valeur est converti en interface et qu’un membre d’interface est accessible. Par exemple, supposons qu’une interface ICounter
contient une méthode Increment
qui peut être utilisée pour modifier une valeur. Si ICounter
elle est utilisée comme contrainte, l’implémentation de la Increment
méthode est appelée avec une référence à la variable qui Increment
a été appelée, et non une copie boxée :
Interface ICounter
Sub Increment()
ReadOnly Property Value() As Integer
End Interface
Structure Counter
Implements ICounter
Dim _value As Integer
Property Value() As Integer Implements ICounter.Value
Get
Return _value
End Get
End Property
Sub Increment() Implements ICounter.Increment
value += 1
End Sub
End Structure
Module Test
Sub Test(Of T As ICounter)(x As T)
Console.WriteLine(x.value)
x.Increment() ' Modify x
Console.WriteLine(x.value)
CType(x, ICounter).Increment() ' Modify boxed copy of x
Console.WriteLine(x.value)
End Sub
Sub Main()
Dim x As Counter
Test(x)
End Sub
End Module
Le premier appel pour Increment
modifier la valeur dans la variable x
. Cela n’équivaut pas au deuxième appel à Increment
, qui modifie la valeur dans une copie boxée de x
. Ainsi, la sortie du programme est la suivante :
0
1
1
Conversions de type valeur nullable
Un type T
valeur peut être converti en et à partir de la version nullable du type. T?
La conversion de T?
vers T
lève une System.InvalidOperationException
exception si la valeur en cours de conversion est Nothing
. En outre, T?
a une conversion en type S
si T
elle a une conversion intrinsèque en S
. Et s’il s’agit S
d’un type valeur, les conversions intrinsèques suivantes existent entre T?
et S?
:
Conversion de la même classification (rétrécissement ou élargissement) de
T?
àS?
.Conversion de la même classification (rétrécissement ou élargissement) de
T
àS?
.Conversion étroite de
S?
versT
.
Par exemple, une conversion d’élargissement intrinsèque existe depuis Integer?
vers, Long?
car une conversion d’élargissement intrinsèque existe de à Long
:Integer
Dim i As Integer? = 10
Dim l As Long? = i
Lors de la conversion en T?
, si la valeur est T?
Nothing
, alors la valeur de S?
sera Nothing
.S?
Lors de la conversion de vers ou vers , si la valeur ou S?
T?
l’estNothing
, une System.InvalidCastException
exception est levée.S
T?
T
S?
En raison du comportement du type System.Nullable(Of T)
sous-jacent , lorsqu’un type T?
valeur nullable est boxé, le résultat est une valeur boxée de type T
, et non une valeur boxed de type T?
. Et, à l’inverse, lors de l’annulation d’unboxing vers un type T?
valeur nullable, la valeur sera encapsulée par System.Nullable(Of T)
, et Nothing
sera non boxée en valeur Null de type T?
. Par exemple:
Dim i1? As Integer = Nothing
Dim o1 As Object = i1
Console.WriteLine(o1 Is Nothing) ' Will print True
o1 = 10
i1 = CType(o1, Integer?)
Console.WriteLine(i1) ' Will print 10
Un effet secondaire de ce comportement est qu’un type T?
valeur nullable apparaît pour implémenter toutes les interfaces de T
, car la conversion d’un type valeur en interface nécessite que le type soit boxé. Par conséquent, T?
il est convertible en toutes les interfaces T
qui sont convertibles. Toutefois, il est important de noter qu’un type T?
valeur nullable n’implémente pas réellement les interfaces à T
des fins de vérification ou de réflexion de contraintes génériques. Par exemple:
Interface I1
End Interface
Structure T1
Implements I1
...
End Structure
Module Test
Sub M1(Of T As I1)(ByVal x As T)
End Sub
Sub Main()
Dim x? As T1 = Nothing
Dim y As I1 = x ' Valid
M1(x) ' Error: x? does not satisfy I1 constraint
End Sub
End Module
Conversions de chaînes
Conversion en Char
String
résultats dans une chaîne dont le premier caractère est la valeur du caractère. Conversion en String
Char
résultats dans un caractère dont la valeur est le premier caractère de la chaîne. Conversion d’un tableau de Char
résultats String
dans une chaîne dont les caractères sont les éléments du tableau.
String
Conversion en tableau de Char
résultats dans un tableau de caractères dont les éléments sont les caractères de la chaîne.
Les conversions exactes entre String
et , , , Short
SByte
UShort
UInteger
Long
Integer
Byte
Decimal
Single
ULong
, Double
, , , Date
, et vice versa, sont au-delà de l’étendue de cette spécification et dépendent de l’implémentation à l’exception d’un détail.Boolean
Les conversions de chaînes prennent toujours en compte la culture actuelle de l’environnement d’exécution. Par conséquent, elles doivent être effectuées au moment de l’exécution.
Conversions étendues
L’élargissement des conversions ne dépasse jamais, mais peut entraîner une perte de précision. Les conversions suivantes sont des conversions étendues :
Conversions d’identité/par défaut
D’un type à lui-même.
À partir d’un type délégué anonyme généré pour une reclassification de méthode lambda vers n’importe quel type délégué avec une signature identique.
Du littéral
Nothing
à un type.
Conversions numériques
De
Byte
àUShort
,Short
UInteger
Integer
ULong
,Long
,Decimal
, ,Single
, ou .Double
De
SByte
versShort
,Integer
,Long
,Decimal
,Single
ouDouble
.De
UShort
versUInteger
,Integer
,ULong
,Long
,Decimal
,Single
ouDouble
.De
Short
àInteger
,Long
, ,Decimal
ouDouble
Single
.De
UInteger
versULong
,Long
,Decimal
,Single
ouDouble
.De
Integer
àLong
,Decimal
ouSingle
Double
.De
ULong
versDecimal
,Single
ouDouble
.De
Long
àDecimal
,Single
ouDouble
.De
Decimal
versSingle
ouDouble
.De
Single
versDouble
.Du littéral
0
à un type énuméré. (Remarque. La conversion de0
n’importe quel type énuméré s’étend pour simplifier les indicateurs de test. Par exemple, s’il s’agitValues
d’un type énuméré avec une valeurOne
, vous pouvez tester une variablev
de typeValues
en disant(v And Values.One) = 0
.)D’un type énuméré à son type numérique sous-jacent, ou à un type numérique vers lequel son type numérique sous-jacent a une conversion étendue.
À partir d’une expression constante de type
ULong
, ,Long
,UShort
Integer
Short
UInteger
,Byte
ouSByte
vers un type plus étroit, à condition que la valeur de l’expression constante se trouve dans la plage du type de destination. (Remarque. Les conversions depuisUInteger
ouInteger
versSingle
,Long
ULong
versSingle
Double
ou vers ouDecimal
vers ou vers ouDouble
peuventSingle
entraîner une perte de précision, mais ne provoqueront jamais une perte de grandeur. Les autres conversions numériques étendues ne perdent jamais d’informations.)
Conversions de référence
D’un type référence à un type de base.
D’un type référence à un type d’interface, à condition que le type implémente l’interface ou une interface compatible variant.
D’un type d’interface à
Object
.D’un type d’interface à un type d’interface compatible variant.
D’un type délégué à un type délégué compatible variant. (Remarque. De nombreuses autres conversions de référence sont implicites par ces règles. Par exemple, les délégués anonymes sont des types de référence qui héritent de
System.MulticastDelegate
; les types de tableau sont des types de référence qui héritent deSystem.Array
; les types anonymes sont des types de référence qui héritent deSystem.Object
.)
Conversions de délégués anonymes
- À partir d’un type délégué anonyme généré pour une reclassification de méthode lambda vers un type délégué plus large.
Conversions de tableaux
D’un type
S
de tableau avec un typeSe
d’élément à un typeT
de tableau avec un typeTe
d’élément , à condition que toutes les valeurs suivantes soient vraies :S
etT
diffèrent uniquement dans le type d’élément.Les deux
Se
types deTe
référence ou sont des paramètres de type connus pour être un type de référence.Une conversion de paramètres de référence, de tableau ou de type étendue existe depuis
Se
Te
.
D’un type
S
de tableau avec un typeSe
d’élément énuméré à un typeT
de tableau avec un typeTe
d’élément , à condition que toutes les valeurs suivantes soient vraies :S
etT
diffèrent uniquement dans le type d’élément.Te
est le type sous-jacent deSe
.
À partir d’un type
S
de tableau de rang 1 avec un typeSe
d’élément énuméré , àSystem.Collections.Generic.IList(Of Te)
,IReadOnlyList(Of Te)
,ICollection(Of Te)
,IReadOnlyCollection(Of Te)
et , fourniIEnumerable(Of Te)
l’un des éléments suivants est vrai :Les deux
Se
types deTe
référence ou sont des paramètres de type connus pour être un type de référence, et une conversion de référence, de tableau ou de paramètre de type étendue existe à partir deSe
;Te
ouTe
est le type sous-jacent deSe
; ouTe
est identique àSe
Conversions de type valeur
D’un type valeur à un type de base.
D’un type valeur à un type d’interface que le type implémente.
Conversions de type valeur nullable
D’un type
T
au typeT?
.D’un type à un type
T?
S?
, où il existe une conversion étendue du type vers le typeS
T
.D’un type à un type
T
S?
, où il existe une conversion étendue du type vers le typeS
T
.D’un type
T?
à un type d’interface que le typeT
implémente.
Conversions de chaînes
De
Char
versString
.De
Char()
versString
.
Conversions de paramètres de type
D’un paramètre de type à
Object
.D’un paramètre de type à une contrainte de type d’interface ou à une variante d’interface compatible avec une contrainte de type d’interface.
D’un paramètre de type à une interface implémentée par une contrainte de classe.
D’un paramètre de type à une variante d’interface compatible avec une interface implémentée par une contrainte de classe.
D’un paramètre de type à une contrainte de classe ou d’un type de base de la contrainte de classe.
D’un paramètre
T
de type à une contrainteTx
de paramètre de type , ou tout aTx
une conversion étendue vers.
conversions restrictives
Les conversions restrictives sont des conversions qui ne peuvent pas être prouvées pour toujours réussir, les conversions connues pour perdre des informations et les conversions entre les domaines de types suffisamment différents pour mériter une notation étroite. Les conversions suivantes sont classées comme des conversions restrictives :
Conversions booléennes
De
Boolean
àByte
,SByte
UShort
Short
,UInteger
,Integer
ULong
Long
Decimal
Single
ou .Double
De
Byte
,SByte
Short
Integer
ULong
UInteger
UShort
Decimal
Single
Long
Double
à .Boolean
Conversions numériques
De
Byte
versSByte
.De
SByte
versByte
,UShort
,UInteger
ouULong
.De
UShort
versByte
,SByte
ouShort
.De
Short
versByte
,SByte
,UShort
,UInteger
ouULong
.De
UInteger
versByte
,SByte
,UShort
,Short
ouInteger
.De
Integer
versByte
,SByte
,UShort
,Short
,UInteger
ouULong
.De
ULong
versByte
,SByte
,UShort
,Short
,UInteger
,Integer
ouLong
.De
Long
versByte
,SByte
,UShort
,Short
,UInteger
,Integer
ouULong
.De
Decimal
versByte
,SByte
,UShort
,Short
,UInteger
,Integer
,ULong
ouLong
.De
Single
àByte
,SByte
UShort
Short
UInteger
,Integer
,ULong
, ,Long
, ou .Decimal
De
Double
àByte
,SByte
UShort
Short
, ,UInteger
Integer
ULong
Long
Decimal
, , ou .Single
D’un type numérique à un type énuméré.
D’un type énuméré à un type numérique dont le type numérique sous-jacent a une conversion étroite en.
D’un type énuméré à un autre type énuméré.
Conversions de référence
D’un type référence à un type plus dérivé.
D’un type de classe à un type d’interface, à condition que le type de classe n’implémente pas le type d’interface ou une variante de type d’interface compatible avec celui-ci.
D’un type d’interface à un type de classe.
D’un type d’interface à un autre type d’interface, à condition qu’il n’existe aucune relation d’héritage entre les deux types et qu’ils ne soient pas compatibles avec les variantes.
Conversions de délégués anonymes
- À partir d’un type délégué anonyme généré pour une reclassification de méthode lambda vers n’importe quel type délégué plus étroit.
Conversions de tableaux
D’un type
S
de tableau avec un typeSe
d’élément , à un typeT
de tableau avec un typeTe
d’élément , à condition que tous les éléments suivants soient vrais :-
S
etT
diffèrent uniquement dans le type d’élément. - Les deux sont des
Se
Te
types de référence ou sont des paramètres de type qui ne sont pas connus pour être des types valeur. - Une conversion de référence, de tableau ou de paramètre de type étroite existe à
Te
partir deSe
.
-
D’un type
S
de tableau avec un typeSe
d’élément à un typeT
de tableau avec un typeTe
d’élément énuméré , à condition que tous les éléments suivants soient vrais :-
S
etT
diffèrent uniquement dans le type d’élément. -
Se
est le type sous-jacent deTe
, ou ils sont tous les deux des types énumérés différents qui partagent le même type sous-jacent.
-
À partir d’un type
S
de tableau de rang 1 avec un typeSe
d’élément énuméré , àIList(Of Te)
,IReadOnlyList(Of Te)
,ICollection(Of Te)
IReadOnlyCollection(Of Te)
et , fourniIEnumerable(Of Te)
l’un des éléments suivants est vrai :- Les deux
Se
types deTe
référence ou sont des paramètres de type connus pour être un type de référence, et une conversion de référence, de tableau ou de paramètre de type étroite existe depuisSe
;Te
ou -
Se
est le type sous-jacent deTe
, ou ils sont tous les deux des types énumérés différents qui partagent le même type sous-jacent.
- Les deux
Conversions de type valeur
D’un type référence à un type valeur plus dérivé.
D’un type d’interface à un type valeur, à condition que le type valeur implémente le type d’interface.
Conversions de type valeur nullable
D’un type
T?
à un typeT
.D’un type à un type
T?
S?
, où il existe une conversion étroite du type vers le typeT
S
.D’un type à un type
T
S?
, où il existe une conversion étroite du type vers le typeT
S
.D’un type à un type
S?
T
, où il existe une conversion du type vers le typeT
S
.
Conversions de chaînes
De
String
versChar
.De
String
versChar()
.De
String
versBoolean
et deBoolean
versString
.Conversions entre
String
etByte
, ,SByte
,Short
ULong
UInteger
UShort
Long
Decimal
Integer
Single
ou .Double
De
String
versDate
et deDate
versString
.
Conversions de paramètres de type
De à un paramètre de
Object
type.D’un paramètre de type à un type d’interface, à condition que le paramètre de type ne soit pas contraint à cette interface ou contraint à une classe qui implémente cette interface.
D’un type d’interface à un paramètre de type.
D’un paramètre de type à un type dérivé d’une contrainte de classe.
D’un paramètre
T
de type à tout ce qu’une contrainteTx
de paramètre de type a une conversion étroite vers.
Conversions de paramètres de type
Les conversions des paramètres de type sont déterminées par les contraintes, le cas échéant, mises sur elles. Un paramètre T
de type peut toujours être converti en lui-même, en et Object
depuis, vers et depuis n’importe quel type d’interface. Notez que si le type T
est un type valeur au moment de l’exécution, la conversion d’un T
type vers Object
ou d’un type d’interface est une conversion de boxing et la conversion à partir Object
d’un type d’interface pour T
être une conversion d’unboxing. Un paramètre de type avec une contrainte C
de classe définit des conversions supplémentaires du paramètre de type vers C
et ses classes de base, et inversement. Un paramètre T
de type avec une contrainte Tx
de paramètre de type définit une conversion vers Tx
et tout ce qui Tx
est converti.
Un tableau dont le type d’élément est un paramètre de type avec une contrainte I
d’interface a les mêmes conversions de tableaux covariants qu’un tableau dont le type d’élément est I
, à condition que le paramètre de type ait également une Class
contrainte ou une contrainte de classe (étant donné que seuls les types d’éléments de tableau de référence peuvent être covariants). Un tableau dont le type d’élément est un paramètre de type avec une contrainte C
de classe a les mêmes conversions de tableaux covariants qu’un tableau dont le type d’élément est C
.
Les règles de conversion ci-dessus n’autorisent pas les conversions de paramètres de type non contraintes en types non-interface, ce qui peut être surprenant. La raison en est d’éviter la confusion quant à la sémantique de ces conversions. Par exemple, considérez la déclaration suivante :
Class X(Of T)
Public Shared Function F(t As T) As Long
Return CLng(t) ' Error, explicit conversion not permitted
End Function
End Class
Si la conversion de T
ces valeurs Integer
était autorisée, on peut s’attendre facilement à ce que cela X(Of Integer).F(7)
retourne 7L
. Toutefois, ce n’est pas le cas, car les conversions numériques ne sont prises en compte que lorsque les types sont connus comme numériques au moment de la compilation. Pour effacer la sémantique, l’exemple ci-dessus doit être écrit à la place :
Class X(Of T)
Public Shared Function F(t As T) As Long
Return CLng(CObj(t)) ' OK, conversions permitted
End Function
End Class
conversions de User-Defined
Les conversions intrinsèques sont définies par le langage (c’est-à-dire répertoriés dans cette spécification), tandis que les conversions définies par l’utilisateur sont définies en surchargeant l’opérateur CType
. Lors de la conversion entre les types, si aucune conversion intrinsèque n’est applicable, les conversions définies par l’utilisateur sont prises en compte. S’il existe une conversion définie par l’utilisateur qui est la plus spécifique pour les types source et cible, la conversion définie par l’utilisateur sera utilisée. Sinon, une erreur au moment de la compilation se produit. La conversion la plus spécifique est celle dont l’opérande est « plus proche » du type source et dont le type de résultat est « le plus proche » du type cible. Lors de la détermination de la conversion définie par l’utilisateur à utiliser, la conversion étendue la plus spécifique sera utilisée ; si aucune conversion étendue n’est plus spécifique, la conversion étroite la plus spécifique sera utilisée. S’il n’existe aucune conversion restrictive la plus spécifique, la conversion n’est pas définie et une erreur au moment de la compilation se produit.
Les sections suivantes décrivent la façon dont les conversions les plus spécifiques sont déterminées. Ils utilisent les termes suivants :
Si une conversion d’élargissement intrinsèque existe d’un type à un type A
B
, et si aucune ni aucune A
interface n’est B
comprise, elle A
est comprise par B
, et B
englobeA
.
Le type le plus englobant dans un ensemble de types est celui qui englobe tous les autres types de l’ensemble. Si aucun type unique n’englobe tous les autres types, l’ensemble n’a pas de type englobant le plus. En termes intuitifs, le type le plus englobant est le type « le plus grand » dans l’ensemble - le type auquel chacun des autres types peut être converti par une conversion étendue.
Le type le plus englobant dans un ensemble de types est celui qui est englobant par tous les autres types de l’ensemble. Si aucun type unique n’est englobant par tous les autres types, l’ensemble n’a pas de type le plus englobant. En termes intuitifs, le type le plus englobant est le type le plus petit dans l’ensemble - celui qui peut être converti en chacun des autres types via une conversion étroite.
Lors de la collecte des conversions définies par l’utilisateur candidat pour un type T?
, les opérateurs de conversion définis par l’utilisateur définis par T
sont utilisés à la place. Si le type en cours de conversion est également un type valeur nullable, les opérateurs de T
conversions définis par l’utilisateur qui impliquent uniquement des types valeur non nullables sont levés. Un opérateur de conversion de T
vers S
est levé pour être une conversion T?
vers S?
et est évalué en convertissant T?
T
en , si nécessaire, en évaluant l’opérateur T
de conversion défini par l’utilisateur de vers S
, puis en convertissant S
en S?
, si nécessaire. Si la valeur en cours de conversion est Nothing
, toutefois, un opérateur de conversion lifted se convertit directement en valeur de Nothing
type S?
. Par exemple:
Structure S
...
End Structure
Structure T
Public Shared Widening Operator CType(ByVal v As T) As S
...
End Operator
End Structure
Module Test
Sub Main()
Dim x As T?
Dim y As S?
y = x ' Legal: y is still null
x = New T()
y = x ' Legal: Converts from T to S
End Sub
End Module
Lors de la résolution des conversions, les opérateurs de conversions définis par l’utilisateur sont toujours préférés aux opérateurs de conversion levés. Par exemple:
Structure S
...
End Structure
Structure T
Public Shared Widening Operator CType(ByVal v As T) As S
...
End Operator
Public Shared Widening Operator CType(ByVal v As T?) As S?
...
End Operator
End Structure
Module Test
Sub Main()
Dim x As T?
Dim y As S?
y = x ' Calls user-defined conversion, not lifted conversion
End Sub
End Module
Au moment de l’exécution, l’évaluation d’une conversion définie par l’utilisateur peut impliquer jusqu’à trois étapes :
Tout d’abord, la valeur est convertie du type source au type d’opérande à l’aide d’une conversion intrinsèque, si nécessaire.
Ensuite, la conversion définie par l’utilisateur est appelée.
Enfin, le résultat de la conversion définie par l’utilisateur est converti en type cible à l’aide d’une conversion intrinsèque, si nécessaire.
Il est important de noter que l’évaluation d’une conversion définie par l’utilisateur n’implique jamais plus d’un opérateur de conversion défini par l’utilisateur.
Conversion d’élargissement la plus spécifique
La détermination de l’opérateur de conversion d’élargissement défini par l’utilisateur le plus spécifique entre deux types est effectuée en procédant comme suit :
Tout d’abord, tous les opérateurs de conversion candidats sont collectés. Les opérateurs de conversion candidats sont tous les opérateurs de conversion élargis définis par l’utilisateur dans le type source et tous les opérateurs de conversion élargis définis par l’utilisateur dans le type cible.
Ensuite, tous les opérateurs de conversion non applicables sont supprimés de l’ensemble. Un opérateur de conversion s’applique à un type source et un type cible s’il existe un opérateur de conversion d’élargissement intrinsèque du type source au type d’opérande et qu’il existe un opérateur de conversion d’élargissement intrinsèque du résultat de l’opérateur vers le type cible. S’il n’existe aucun opérateur de conversion applicable, il n’existe aucune conversion étendue la plus spécifique.
Ensuite, le type source le plus spécifique des opérateurs de conversion applicables est déterminé :
Si l’un des opérateurs de conversion convertit directement à partir du type source, le type source est le type source le plus spécifique.
Sinon, le type source le plus spécifique est le type le plus englobant dans l’ensemble combiné de types sources des opérateurs de conversion. Si aucun type le plus englobant n’est trouvé, il n’existe aucune conversion étendue la plus spécifique.
Ensuite, le type cible le plus spécifique des opérateurs de conversion applicables est déterminé :
Si l’un des opérateurs de conversion se convertit directement en type cible, le type cible est le type cible le plus spécifique.
Sinon, le type cible le plus spécifique est le type le plus englobant dans l’ensemble combiné de types cibles des opérateurs de conversion. Si aucun type englobant n’est trouvé, il n’existe aucune conversion étendue la plus spécifique.
Ensuite, si exactement un opérateur de conversion convertit du type source le plus spécifique au type cible le plus spécifique, il s’agit de l’opérateur de conversion le plus spécifique. Si plusieurs opérateurs de ce type existent, il n’existe aucune conversion étendue la plus spécifique.
Conversion étroite la plus spécifique
La détermination de l’opérateur de conversion étroit défini par l’utilisateur le plus spécifique entre deux types est effectuée en procédant comme suit :
Tout d’abord, tous les opérateurs de conversion candidats sont collectés. Les opérateurs de conversion candidats sont tous les opérateurs de conversion définis par l’utilisateur dans le type source et tous les opérateurs de conversion définis par l’utilisateur dans le type cible.
Ensuite, tous les opérateurs de conversion non applicables sont supprimés de l’ensemble. Un opérateur de conversion s’applique à un type source et à un type cible s’il existe un opérateur de conversion intrinsèque du type source vers le type d’opérande et qu’il existe un opérateur de conversion intrinsèque du résultat de l’opérateur vers le type cible. S’il n’existe aucun opérateur de conversion applicable, il n’existe aucune conversion étroite la plus spécifique.
Ensuite, le type source le plus spécifique des opérateurs de conversion applicables est déterminé :
Si l’un des opérateurs de conversion convertit directement à partir du type source, le type source est le type source le plus spécifique.
Sinon, si l’un des opérateurs de conversion convertit à partir de types qui englobent le type source, le type source le plus spécifique est le type le plus englobant dans l’ensemble combiné de types sources de ces opérateurs de conversion. Si aucun type le plus englobant n’est trouvé, il n’existe aucune conversion étroite la plus spécifique.
Sinon, le type source le plus spécifique est le type le plus englobant dans l’ensemble combiné de types sources des opérateurs de conversion. Si aucun type englobant n’est trouvé, il n’existe aucune conversion étroite la plus spécifique.
Ensuite, le type cible le plus spécifique des opérateurs de conversion applicables est déterminé :
Si l’un des opérateurs de conversion se convertit directement en type cible, le type cible est le type cible le plus spécifique.
Dans le cas contraire, si l’un des opérateurs de conversion convertit en types englobants par le type cible, le type cible le plus spécifique est le type le plus englobant dans l’ensemble combiné de types sources de ces opérateurs de conversion. Si aucun type englobant n’est trouvé, il n’existe aucune conversion étroite la plus spécifique.
Sinon, le type cible le plus spécifique est le type le plus englobant dans l’ensemble combiné de types cibles des opérateurs de conversion. Si aucun type le plus englobant n’est trouvé, il n’existe aucune conversion étroite la plus spécifique.
Ensuite, si exactement un opérateur de conversion convertit du type source le plus spécifique au type cible le plus spécifique, il s’agit de l’opérateur de conversion le plus spécifique. Si plusieurs opérateurs de ce type existent, il n’existe aucune conversion étroite la plus spécifique.
Conversions natives
Plusieurs conversions sont classées comme conversions natives , car elles sont prises en charge en mode natif par le .NET Framework. Ces conversions sont celles qui peuvent être optimisées par le biais de l’utilisation DirectCast
des opérateurs et TryCast
des opérateurs de conversion, ainsi que d’autres comportements spéciaux. Les conversions classées comme conversions natives sont les suivantes : conversions d’identité, conversions par défaut, conversions de référence, conversions de tableau, conversions de type valeur et conversions de paramètres de type.
Dominant Type
Étant donné un ensemble de types, il est souvent nécessaire dans des situations telles que l’inférence de type pour déterminer le type dominant du jeu. Le type dominant d’un ensemble de types est déterminé en supprimant d’abord tous les types vers utilisant un ou plusieurs autres types. S’il n’y a aucun type laissé à ce stade, il n’y a pas de type dominant. Le type dominant est alors le plus englobant des types restants. S’il existe plusieurs types qui sont les plus englobants, il n’y a pas de type dominant.
Visual Basic language spec