Note
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier les répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de changer de répertoire.
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 DerivedDoublepeut être convertie en valeur de typeBase, en supposant que BaseDerived 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 IntegerString). 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 IntegerLong 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 True255 pour Byte, 65535 pour UShort, 4294967295 pour UInteger, 18446744073709551615 pour ULong, et en l’expression -1 pour SByte, Short, Integer, , Long, Decimal, Singleet 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 TrueStringFalseString (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, UShortIntegerUIntegerSByteShortULong, Long, Decimal, Single et Doubletous 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, ,IntegerULong,LongouDecimalversSingleouDoublesont arrondies à la valeur ouDoublela plusSingleproche. 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,DoubleouDecimalvers 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.OverflowExceptionexception si l’argument source est en dehors de la plage du type de destination.Si la source est
Single,DoubleouDecimal, 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.OverflowExceptionexception 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,DoubleouDecimal, 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
DoubleversSingle, laDoublevaleur est arrondie à la valeur la plusSingleproche. Si laDoublevaleur est trop petite pour représenter sous la forme d’unSingle, le résultat devient zéro positif ou zéro négatif. Si laDoublevaleur est trop grande pour représenter en tant queSingle, le résultat devient l’infini positif ou l’infini négatif. Si laDoublevaleur estNaN, le résultat est égalementNaN.Pour une conversion de
SingleouDoubleversDecimal, la valeur source est convertie enDecimalrepré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.OverflowExceptionexception est levée.Pour une conversion de
DoubleversSingle, laDoublevaleur est arrondie à la valeur la plusSingleproche. Si laDoublevaleur est trop petite pour représenter sous la forme d’unSingle, le résultat devient zéro positif ou zéro négatif. Si laDoublevaleur est trop grande pour représenter en tant queSingle, le résultat devient l’infini positif ou l’infini négatif. Si laDoublevaleur 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 :
SetTsont tous deux construits à partir du même typeU(Of U1,...,Un)générique.Pour chaque paramètre
Uxde type :Si le paramètre de type a été déclaré sans variance,
SxetTxdoit ê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 àTxpartir 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 àSxpartir 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 + bou 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 Stringb , 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 :
-
AetBsont à la fois des types de référence ou des paramètres de type non connus comme des types valeur ; etAont une conversion de référence, de tableau ou de paramètre de type étendue enB; ou -
AetBsont tous deux des types énumérés du même type sous-jacent ; ou - l’un des
Atypes énumérés etBl’autre est son type sous-jacent.
Tout tableau de type A avec n’importe quel rang a également une conversion de tableau en types IListd’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 Eachde , 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 IListICollection, 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 IListdé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.ValueTypedé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.ST?TS?
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 CharString résultats dans une chaîne dont le premier caractère est la valeur du caractère. Conversion en StringChar 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 , , , ShortSByteUShortUIntegerLongIntegerByteDecimalSingleULong, 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,ShortUIntegerIntegerULong,Long,Decimal, ,Single, ou .DoubleDe
SByteversShort,Integer,Long,Decimal,SingleouDouble.De
UShortversUInteger,Integer,ULong,Long,Decimal,SingleouDouble.De
ShortàInteger,Long, ,DecimalouDoubleSingle.De
UIntegerversULong,Long,Decimal,SingleouDouble.De
IntegeràLong,DecimalouSingleDouble.De
ULongversDecimal,SingleouDouble.De
LongàDecimal,SingleouDouble.De
DecimalversSingleouDouble.De
SingleversDouble.Du littéral
0à un type énuméré. (Remarque. La conversion de0n’importe quel type énuméré s’étend pour simplifier les indicateurs de test. Par exemple, s’il s’agitValuesd’un type énuméré avec une valeurOne, vous pouvez tester une variablevde typeValuesen 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,UShortIntegerShortUInteger,ByteouSBytevers un type plus étroit, à condition que la valeur de l’expression constante se trouve dans la plage du type de destination. (Remarque. Les conversions depuisUIntegerouIntegerversSingle,LongULongversSingleDoubleou vers ouDecimalvers ou vers ouDoublepeuventSingleentraî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
Sde tableau avec un typeSed’élément à un typeTde tableau avec un typeTed’élément , à condition que toutes les valeurs suivantes soient vraies :SetTdiffèrent uniquement dans le type d’élément.Les deux
Setypes deTeré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
SeTe.
D’un type
Sde tableau avec un typeSed’élément énuméré à un typeTde tableau avec un typeTed’élément , à condition que toutes les valeurs suivantes soient vraies :SetTdiffèrent uniquement dans le type d’élément.Teest le type sous-jacent deSe.
À partir d’un type
Sde tableau de rang 1 avec un typeSed’é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
Setypes deTeré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;TeouTeest le type sous-jacent deSe; ouTeest 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
Tau typeT?.D’un type à un type
T?S?, où il existe une conversion étendue du type vers le typeST.D’un type à un type
TS?, où il existe une conversion étendue du type vers le typeST.D’un type
T?à un type d’interface que le typeTimplémente.
Conversions de chaînes
De
CharversString.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
Tde type à une contrainteTxde paramètre de type , ou tout aTxune 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,SByteUShortShort,UInteger,IntegerULongLongDecimalSingleou .DoubleDe
Byte,SByteShortIntegerULongUIntegerUShortDecimalSingleLongDoubleà .Boolean
Conversions numériques
De
ByteversSByte.De
SByteversByte,UShort,UIntegerouULong.De
UShortversByte,SByteouShort.De
ShortversByte,SByte,UShort,UIntegerouULong.De
UIntegerversByte,SByte,UShort,ShortouInteger.De
IntegerversByte,SByte,UShort,Short,UIntegerouULong.De
ULongversByte,SByte,UShort,Short,UInteger,IntegerouLong.De
LongversByte,SByte,UShort,Short,UInteger,IntegerouULong.De
DecimalversByte,SByte,UShort,Short,UInteger,Integer,ULongouLong.De
SingleàByte,SByteUShortShortUInteger,Integer,ULong, ,Long, ou .DecimalDe
DoubleàByte,SByteUShortShort, ,UIntegerIntegerULongLongDecimal, , ou .SingleD’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
Sde tableau avec un typeSed’élément , à un typeTde tableau avec un typeTed’élément , à condition que tous les éléments suivants soient vrais :-
SetTdiffèrent uniquement dans le type d’élément. - Les deux sont des
SeTetypes 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 à
Tepartir deSe.
-
D’un type
Sde tableau avec un typeSed’élément à un typeTde tableau avec un typeTed’élément énuméré , à condition que tous les éléments suivants soient vrais :-
SetTdiffèrent uniquement dans le type d’élément. -
Seest 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
Sde tableau de rang 1 avec un typeSed’é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
Setypes deTeré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;Teou -
Seest 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 typeTS.D’un type à un type
TS?, où il existe une conversion étroite du type vers le typeTS.D’un type à un type
S?T, où il existe une conversion du type vers le typeTS.
Conversions de chaînes
De
StringversChar.De
StringversChar().De
StringversBooleanet deBooleanversString.Conversions entre
StringetByte, ,SByte,ShortULongUIntegerUShortLongDecimalIntegerSingleou .DoubleDe
StringversDateet deDateversString.
Conversions de paramètres de type
De à un paramètre de
Objecttype.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
Tde type à tout ce qu’une contrainteTxde 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 Objectdepuis, 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 AB, et si aucune ni aucune A interface n’est B comprise, elle A est comprise par B, et BenglobeA.
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 Tconversions 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?Ten , 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