Partager via


Conversions en Visual Basic

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, Longou Decimal vers Single ou Double sont arrondies à la valeur ou Double la plus Single 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, Doubleou Decimal 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, Doubleou Decimal, 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, une System.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, Doubleou Decimal, 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 vers Single, la Double valeur est arrondie à la valeur la plus Single proche. Si la Double valeur est trop petite pour représenter sous la forme d’un Single, le résultat devient zéro positif ou zéro négatif. Si la Double valeur est trop grande pour représenter en tant que Single, le résultat devient l’infini positif ou l’infini négatif. Si la Double valeur est NaN, le résultat est également NaN.

  • Pour une conversion de Single ou Double vers Decimal, la valeur source est convertie en Decimal 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’un Decimal, le résultat devient zéro. Si la valeur source est NaN, l’infini ou trop grand pour représenter sous la forme d’un Decimal, une System.OverflowException exception est levée.

  • Pour une conversion de Double vers Single, la Double valeur est arrondie à la valeur la plus Single proche. Si la Double valeur est trop petite pour représenter sous la forme d’un Single, le résultat devient zéro positif ou zéro négatif. Si la Double valeur est trop grande pour représenter en tant que Single, le résultat devient l’infini positif ou l’infini négatif. Si la Double valeur est NaN, le résultat est également NaN.

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 et T sont tous deux construits à partir du même type U(Of U1,...,Un)générique.

  • Pour chaque paramètre Uxde type :

    • Si le paramètre de type a été déclaré sans variance, Sx et Tx 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 à Txpartir de Sx .

    • 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 de Tx .

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 :

  • A et B sont à la fois des types de référence ou des paramètres de type non connus comme des types valeur ; et A ont une conversion de référence, de tableau ou de paramètre de type étendue en B; ou
  • A et B sont tous deux des types énumérés du même type sous-jacent ; ou
  • l’un des A types énumérés et B 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 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? vers T.

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 .Double

  • De SByte vers Short, Integer, Long, Decimal, Single ou Double.

  • De UShort vers UInteger, Integer, ULong, Long, Decimal, Single ou Double.

  • De Short à Integer, Long, , Decimalou DoubleSingle .

  • De UInteger vers ULong, Long, Decimal, Single ou Double.

  • De Integer à Long, Decimalou SingleDouble.

  • De ULong vers Decimal, Single ou Double.

  • De Long à Decimal, Single ou Double.

  • De Decimal vers Single ou Double.

  • De Single vers Double.

  • Du littéral 0 à un type énuméré. (Remarque. La conversion de 0 n’importe quel type énuméré s’étend pour simplifier les indicateurs de test. Par exemple, s’il s’agit Values d’un type énuméré avec une valeur One, vous pouvez tester une variable v de type Values 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, UShortIntegerShortUInteger, Byteou SByte 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 depuis UInteger ou Integer versSingle, LongULong vers SingleDoubleou vers ou Decimal vers ou vers ou Double peuvent Single 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 de System.Array; les types anonymes sont des types de référence qui héritent de System.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 type Se d’élément à un type T de tableau avec un type Ted’élément , à condition que toutes les valeurs suivantes soient vraies :

    • S et T diffèrent uniquement dans le type d’élément.

    • Les deux Se types de Te 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 SeTe.

  • D’un type S de tableau avec un type Se d’élément énuméré à un type T de tableau avec un type Ted’élément , à condition que toutes les valeurs suivantes soient vraies :

    • S et T diffèrent uniquement dans le type d’élément.

    • Te est le type sous-jacent de Se.

  • À partir d’un type S de tableau de rang 1 avec un type Sed’élément énuméré , à System.Collections.Generic.IList(Of Te), IReadOnlyList(Of Te), ICollection(Of Te), IReadOnlyCollection(Of Te)et , fourni IEnumerable(Of Te)l’un des éléments suivants est vrai :

    • Les deux Se types de Te 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 de Se ; Teou

    • Te est le type sous-jacent de Se; ou

    • Te 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 type T?.

  • D’un type à un type T?S?, où il existe une conversion étendue du type vers le type ST .

  • D’un type à un type TS?, où il existe une conversion étendue du type vers le type ST .

  • D’un type T? à un type d’interface que le type T implémente.

Conversions de chaînes

  • De Char vers String.

  • De Char() vers String.

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 contrainte Txde paramètre de type , ou tout a Tx 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, SByteUShortShort, UInteger, IntegerULongLongDecimalSingleou .Double

  • De Byte, SByteShortIntegerULongUIntegerUShortDecimalSingleLongDouble à .Boolean

Conversions numériques

  • De Byte vers SByte.

  • De SByte vers Byte, UShort, UInteger ou ULong.

  • De UShort vers Byte, SByte ou Short.

  • De Short vers Byte, SByte, UShort, UInteger ou ULong.

  • De UInteger vers Byte, SByte, UShort, Short ou Integer.

  • De Integer vers Byte, SByte, UShort, Short, UInteger ou ULong.

  • De ULong vers Byte, SByte, UShort, Short, UInteger, Integer ou Long.

  • De Long vers Byte, SByte, UShort, Short, UInteger, Integer ou ULong.

  • De Decimal vers Byte, SByte, UShort, Short, UInteger, Integer, ULong ou Long.

  • De Single à Byte, SByteUShortShortUInteger, Integer, ULong, , Long, ou .Decimal

  • De Double à Byte, SByteUShortShort, , UIntegerIntegerULongLongDecimal, , 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 type Sed’élément , à un type T de tableau avec un type Ted’élément , à condition que tous les éléments suivants soient vrais :

    • S et T diffèrent uniquement dans le type d’élément.
    • Les deux sont des SeTe 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 à Tepartir de Se .
  • D’un type S de tableau avec un type Se d’élément à un type T de tableau avec un type Ted’élément énuméré , à condition que tous les éléments suivants soient vrais :

    • S et T diffèrent uniquement dans le type d’élément.
    • Se est le type sous-jacent de Te , 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 type Sed’élément énuméré , à IList(Of Te), IReadOnlyList(Of Te), ICollection(Of Te)IReadOnlyCollection(Of Te) et , fourni IEnumerable(Of Te)l’un des éléments suivants est vrai :

    • Les deux Se types de Te 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 depuis Se ; Teou
    • Se est le type sous-jacent de Te, ou ils sont tous les deux des types énumérés différents qui partagent le même type sous-jacent.

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 type T.

  • D’un type à un type T?S?, où il existe une conversion étroite du type vers le type TS.

  • D’un type à un type TS?, où il existe une conversion étroite du type vers le type TS.

  • D’un type à un type S?T, où il existe une conversion du type vers le type TS .

Conversions de chaînes

  • De String vers Char.

  • De String vers Char().

  • De String vers Boolean et de Boolean vers String.

  • Conversions entre String et Byte, , SByte, ShortULongUIntegerUShortLongDecimalIntegerSingleou .Double

  • De String vers Date et de Date vers String.

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 contrainte Tx 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 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 :

  1. 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.

  2. Ensuite, la conversion définie par l’utilisateur est appelée.

  3. 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 :

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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 :

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.