Condividi tramite


Conversioni in Visual Basic

La conversione è il processo di modifica di un valore da un tipo a un altro. Ad esempio, un valore di tipo Integer può essere convertito in un valore di tipo Doubleo un valore di tipo Derived può essere convertito in un valore di tipo Base, presupponendo che Base e Derived siano entrambe classi e Derived ereditano da Base. Le conversioni potrebbero non richiedere la modifica del valore stesso (come nell'ultimo esempio) oppure potrebbero richiedere modifiche significative nella rappresentazione del valore (come nell'esempio precedente).

Le conversioni possono essere più grandi o più strette. Una conversione verso un tipo più grande è una conversione da un tipo a un altro tipo il cui dominio valore è almeno grande, se non più grande, rispetto al dominio del valore del tipo originale. Le conversioni con estensione non dovrebbero mai avere esito negativo. Una conversione di tipo narrowing è una conversione da un tipo a un altro tipo il cui dominio di valore è minore del dominio del valore del tipo originale o sufficientemente non correlato che è necessario prestare particolare attenzione quando si esegue la conversione , ad esempio quando si esegue la conversione da Integer a String. Le conversioni di tipo narrowing, che possono comportare la perdita di informazioni, possono non riuscire.

La conversione di identità (ad esempio una conversione da un tipo a se stessa) e la conversione di valori predefiniti (ad esempio una conversione da Nothing) sono definite per tutti i tipi.

Conversioni implicite ed esplicite

Le conversioni possono essere implicite o esplicite. Le conversioni implicite vengono eseguite senza sintassi speciale. Di seguito è riportato un esempio di conversione implicita di un Integer valore in un Long valore:

Module Test
    Sub Main()
        Dim intValue As Integer = 123
        Dim longValue As Long = intValue

        Console.WriteLine(intValue & " = " & longValue)
    End Sub
End Module

Le conversioni esplicite, d'altra parte, richiedono operatori cast. Se si tenta di eseguire una conversione esplicita su un valore senza un operatore cast, viene generato un errore in fase di compilazione. Nell'esempio seguente viene utilizzata una conversione esplicita per convertire un Long valore in un Integer valore.

Module Test
    Sub Main()
        Dim longValue As Long = 134
        Dim intValue As Integer = CInt(longValue)

        Console.WriteLine(longValue & " = " & intValue)
    End Sub
End Module

Il set di conversioni implicite dipende dall'ambiente di compilazione e dall'istruzione Option Strict . Se viene usata una semantica rigorosa, è possibile che vengano eseguite in modo implicito solo conversioni più ampliate. Se viene usata una semantica permissiva, tutte le conversioni verso un tipo più ampio e più ristretto (in altre parole, tutte le conversioni) possono verificarsi in modo implicito.

Conversioni booleane

Anche se Boolean non è un tipo numerico, presenta conversioni verso e verso i tipi numerici come se fosse un tipo enumerato. Il valore letterale True viene convertito nel valore letterale 255 per Byte, 65535 per UShort, 4294967295 per UInteger18446744073709551615ULonge nell'espressione -1 per SByte, ShortLongDecimalIntegerSinglee .Double Il valore letterale False viene convertito nel valore letterale 0. Un valore numerico zero viene convertito nel valore letterale False. Tutti gli altri valori numerici vengono convertiti nel valore letterale True.

È presente una conversione di tipo narrowing da Boolean a String, convertendo in System.Boolean.TrueString o System.Boolean.FalseString. Esiste anche una conversione verso un tipo di dati più piccolo da String a Boolean: se la stringa è uguale a TrueString o FalseString (nelle impostazioni cultura correnti, senza distinzione tra maiuscole e minuscole) usa il valore appropriato; in caso contrario, tenta di analizzare la stringa come tipo numerico (in esadecimale o ottale, se possibile, altrimenti come float) e usa le regole precedenti; in caso contrario, genera System.InvalidCastException.

Conversioni numeriche

Esistono conversioni numeriche tra i tipi Byte, SByte, ShortUShort, , IntegerUInteger, ULongLongDecimalSingle e Doublee tutti i tipi enumerati. Durante la conversione, i tipi enumerati vengono considerati come se fossero i tipi sottostanti. Quando si esegue la conversione in un tipo enumerato, il valore di origine non è necessario per essere conforme al set di valori definiti nel tipo enumerato. Per esempio:

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

Le conversioni numeriche vengono elaborate in fase di esecuzione come indicato di seguito:

  • Per una conversione da un tipo numerico a un tipo numerico più ampio, il valore viene semplicemente convertito nel tipo più ampio. Le conversioni da UInteger, ULongInteger, , Longo Decimal a Single o Double vengono arrotondate al valore o Double più Single vicino. Anche se questa conversione può causare una perdita di precisione, non causerà mai una perdita di grandezza.

  • Per una conversione da un tipo integrale a un altro tipo integrale o da Single, Doubleo Decimal a un tipo integrale, il risultato dipende dal fatto che il controllo dell'overflow integer sia attivo:

    Se viene controllato l'overflow integer:

    • Se l'origine è un tipo integrale, la conversione ha esito positivo se l'argomento di origine è compreso nell'intervallo del tipo di destinazione. La conversione genera un'eccezione System.OverflowException se l'argomento di origine non è compreso nell'intervallo del tipo di destinazione.

    • Se l'origine è Single, Doubleo Decimal, il valore di origine viene arrotondato verso l'alto o verso il basso al valore integrale più vicino e questo valore integrale diventa il risultato della conversione. Se il valore di origine è ugualmente vicino a due valori integrali, il valore viene arrotondato al valore con un numero pari nella posizione della cifra meno significativa. Se il valore integrale risultante non è compreso nell'intervallo del tipo di destinazione, viene generata un'eccezione System.OverflowException .

    Se l'overflow integer non viene controllato:

    • Se l'origine è un tipo integrale, la conversione ha sempre esito positivo e consiste semplicemente nell'eliminare i bit più significativi del valore di origine.

    • Se l'origine è Single, Doubleo Decimal, la conversione ha sempre esito positivo e consiste semplicemente nell'arrotondare il valore di origine verso il valore integrale più vicino. Se il valore di origine è ugualmente vicino a due valori integrali, il valore viene sempre arrotondato al valore con un numero pari nella posizione della cifra meno significativa.

  • Per una conversione da Double a Single, il Double valore viene arrotondato al valore più Single vicino. Se il Double valore è troppo piccolo per rappresentare come Single, il risultato diventa zero positivo o negativo zero. Se il Double valore è troppo grande per rappresentare come Single, il risultato diventa infinito positivo o infinito negativo. Se il Double valore è NaN, il risultato è anche NaN.

  • Per una conversione da Single o Double in Decimal, il valore di origine viene convertito in Decimal rappresentazione e arrotondato al numero più vicino dopo il 28° separatore decimale, se necessario. Se il valore di origine è troppo piccolo per rappresentare come Decimal, il risultato diventa zero. Se il valore di origine è NaN, infinito o troppo grande da rappresentare come Decimal, viene generata un'eccezione System.OverflowException .

  • Per una conversione da Double a Single, il Double valore viene arrotondato al valore più Single vicino. Se il Double valore è troppo piccolo per rappresentare come Single, il risultato diventa zero positivo o negativo zero. Se il Double valore è troppo grande per rappresentare come Single, il risultato diventa infinito positivo o infinito negativo. Se il Double valore è NaN, il risultato è anche NaN.

Conversioni di riferimento

I tipi riferimento possono essere convertiti in un tipo di base e viceversa. Le conversioni da un tipo di base a un tipo più derivato hanno esito positivo solo in fase di esecuzione se il valore convertito è un valore Null, il tipo derivato stesso o un tipo più derivato.

È possibile eseguire il cast di tipi di classe e di interfaccia da e verso qualsiasi tipo di interfaccia. Le conversioni tra un tipo e un tipo di interfaccia hanno esito positivo solo in fase di esecuzione se i tipi effettivi coinvolti hanno una relazione di ereditarietà o implementazione. Poiché un tipo di interfaccia conterrà sempre un'istanza di un tipo che deriva da Object, è sempre possibile eseguire il cast di un tipo di interfaccia da e verso Object.

Nota. Non è un errore convertire classi NotInheritable in e da interfacce che non implementa perché le classi che rappresentano classi COM possono avere implementazioni di interfaccia che non sono note fino al runtime.

Se una conversione di riferimento non riesce in fase di esecuzione, viene generata un'eccezione System.InvalidCastException .

Conversioni di varianza di riferimento

Le interfacce generiche o i delegati possono avere parametri di tipo varianti che consentono conversioni tra varianti compatibili del tipo. Pertanto, in fase di esecuzione, una conversione da un tipo di classe o un tipo di interfaccia a un tipo di interfaccia compatibile con un tipo di interfaccia che eredita o implementa avrà esito positivo. Analogamente, è possibile eseguire il cast dei tipi delegati da e verso tipi delegati compatibili con varianti. Ad esempio, il tipo delegato

Delegate Function F(Of In A, Out R)(a As A) As R

consente una conversione da F(Of Object, Integer) a F(Of String, Integer). Ovvero, un delegato F che accetta Object può essere usato in modo sicuro come delegato F che accetta String. Quando il delegato viene richiamato, il metodo di destinazione prevede un oggetto e una stringa è un oggetto .

Un tipo di S(Of S1,...,Sn) interfaccia o delegato generico è detto compatibile con varianti con un'interfaccia generica o un tipo T(Of T1,...,Tn) delegato se:

  • S e T sono entrambi costruiti dallo stesso tipo U(Of U1,...,Un)generico .

  • Per ogni parametro Uxdi tipo :

    • Se il parametro di tipo è stato dichiarato senza varianza Sx e Tx deve essere lo stesso tipo.

    • Se il parametro di tipo è stato dichiarato In , deve essere presente una conversione di parametri di tipo, default, reference, array o type da Sx a Tx.

    • Se il parametro di tipo è stato dichiarato Out , deve essere presente una conversione di parametri di tipo, default, reference, array o type da Tx a Sx.

Quando si esegue la conversione da una classe a un'interfaccia generica con parametri di tipo varianti, se la classe implementa più interfacce compatibili con varianti, la conversione è ambigua se non è presente una conversione non variante. Per esempio:

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

Conversioni di delegati anonimi

Quando un'espressione classificata come metodo lambda viene riclassificata come valore in un contesto in cui non è presente alcun tipo di destinazione ( ad esempio , Dim x = Function(a As Integer, b As Integer) a + b) o dove il tipo di destinazione non è un tipo delegato, il tipo dell'espressione risultante è un tipo delegato anonimo equivalente alla firma del metodo lambda. Questo tipo delegato anonimo ha una conversione a qualsiasi tipo delegato compatibile: un tipo delegato compatibile è qualsiasi tipo delegato che può essere creato usando un'espressione di creazione del delegato con il metodo del Invoke tipo delegato anonimo come parametro. Per esempio:

' 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

Si noti che i tipi System.Delegate e System.MulticastDelegate non sono considerati tipi delegati (anche se tutti i tipi delegati ereditano da essi). Si noti inoltre che la conversione dal tipo delegato anonimo a un tipo delegato compatibile non è una conversione di riferimento.

Conversioni di matrici

Oltre alle conversioni definite sulle matrici in virtù del fatto che sono tipi di riferimento, esistono diverse conversioni speciali per le matrici.

Per due tipi A e B, se sono entrambi tipi riferimento o parametri di tipo non noti come tipi valore e se A ha una conversione di parametri riferimento, matrice o tipo in B, esiste una conversione da una matrice di tipo A a una matrice di tipo B con lo stesso rango. Questa relazione è nota come covarianza della matrice. La covarianza della matrice in particolare indica che un elemento di una matrice il cui tipo di elemento è B in realtà può essere un elemento di una matrice il cui tipo di elemento è A, purché sia A che B siano tipi riferimento e che B dispongano di una conversione di riferimento o di una conversione di matrice in A. Nell'esempio seguente, la seconda chiamata di F fa sì che venga generata un'eccezione System.ArrayTypeMismatchException perché il tipo di elemento effettivo di b è String, 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

A causa della covarianza della matrice, le assegnazioni agli elementi delle matrici di tipi di riferimento includono un controllo di runtime che garantisce che il valore assegnato all'elemento della matrice sia effettivamente di un tipo consentito.

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

In questo esempio l'assegnazione a array(i) nel metodo Fill include in modo implicito un controllo di runtime che garantisce che l'oggetto a cui fa riferimento la variabile value sia Nothing o un'istanza di un tipo compatibile con il tipo di elemento effettivo della matrice array. Nel metodo Main, le prime due chiamate del metodo Fill hanno esito positivo, ma la terza chiamata genera un'eccezione System.ArrayTypeMismatchException quando si esegue la prima assegnazione a array(i). L'eccezione si verifica perché un oggetto Integer non può essere archiviato in una String matrice.

Se uno dei tipi di elemento matrice è un parametro di tipo il cui tipo risulta essere un tipo valore in fase di esecuzione, verrà generata un'eccezione System.InvalidCastException . Per esempio:

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

Esistono anche conversioni tra una matrice di un tipo enumerato e una matrice del tipo sottostante dell'enumerato o una matrice di un altro tipo enumerato con lo stesso tipo sottostante, a condizione che le matrici abbiano lo stesso rango.

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

In questo esempio, una matrice di Color viene convertita in e da una matrice di Bytetipo sottostante di . Color La conversione in una matrice di Integer, tuttavia, sarà un errore perché Integer non è il tipo sottostante di Color.

Una matrice di tipo A() rank-1 include anche una conversione di matrice nei tipi di IList(Of B)interfaccia di raccolta , , IReadOnlyCollection(Of B)IReadOnlyList(Of B)ICollection(Of B)e IEnumerable(Of B) presenti in System.Collections.Generic, purché una delle condizioni seguenti sia vera:

  • A e B sono entrambi tipi di riferimento o parametri di tipo non noti come tipi di valore; e A ha una conversione di parametri di tipo, matrice o di tipo più ampia in B; o
  • A e B sono entrambi tipi enumerati dello stesso tipo sottostante; o
  • uno di A e B è un tipo enumerato e l'altro è il tipo sottostante.

Qualsiasi matrice di tipo A con qualsiasi rango ha anche una conversione di matrice nei tipi di IListinterfaccia di raccolta non generici e ICollectionIEnumerable disponibile in System.Collections.

È possibile scorrere le interfacce risultanti usando For Eacho richiamare direttamente i GetEnumerator metodi. Nel caso di matrici di rango 1 convertite in forme generiche o non generiche di IList o ICollection, è anche possibile ottenere elementi in base all'indice. Nel caso di matrici rank-1 convertite in forme generiche o non generiche di IList, è anche possibile impostare gli elementi per indice, soggetti agli stessi controlli di covarianza della matrice di runtime come descritto in precedenza. Il comportamento di tutti gli altri metodi di interfaccia non è definito dalla specifica del linguaggio VB; spetta al runtime sottostante.

Conversioni dei tipi di valore

Un valore di tipo valore può essere convertito in uno dei relativi tipi di riferimento di base o in un tipo di interfaccia implementato tramite un processo denominato boxing. Quando viene sottoposto a boxing un valore di tipo valore, il valore viene copiato dalla posizione in cui si trova nell'heap di .NET Framework. Viene quindi restituito un riferimento a questa posizione nell'heap e può essere archiviato in una variabile di tipo riferimento. Questo riferimento viene anche definito istanza boxed del tipo di valore. L'istanza boxed ha la stessa semantica di un tipo riferimento anziché un tipo di valore.

I tipi valore boxed possono essere convertiti di nuovo nel tipo di valore originale tramite un processo denominato unboxing. Quando un tipo di valore boxed è unboxed, il valore viene copiato dall'heap in una posizione variabile. Da quel punto in poi, si comporta come se fosse un tipo di valore. Quando si unboxing di un tipo valore, il valore deve essere un valore Null o un'istanza del tipo di valore. In caso contrario, viene generata un'eccezione System.InvalidCastException . Se il valore è un'istanza di un tipo enumerato, tale valore può anche essere sottoposto a unboxing nel tipo sottostante del tipo enumerato o in un altro tipo enumerato con lo stesso tipo sottostante. Un valore Null viene considerato come se fosse il valore letterale Nothing.

Per supportare correttamente i tipi valore nullable, il tipo di System.Nullable(Of T) valore viene trattato appositamente quando si esegue la conversione boxing e unboxing. Il boxing di un valore di tipo Nullable(Of T) restituisce un valore boxed di tipo T se la proprietà del HasValue valore è True o un valore di Nothing se la proprietà del HasValue valore è False. Unboxing di un valore di tipo T per Nullable(Of T) ottenere un'istanza della Nullable(Of T) cui Value proprietà è il valore boxed e la cui HasValue proprietà è True. Il valore Nothing può essere unboxed in Nullable(Of T) per qualsiasi T oggetto e restituisce un valore la cui HasValue proprietà è False. Poiché i tipi valore boxed si comportano come tipi riferimento, è possibile creare più riferimenti allo stesso valore. Per i tipi primitivi e i tipi enumerati, questo è irrilevante perché le istanze di tali tipi non sono modificabili. Ciò significa che non è possibile modificare un'istanza boxed di tali tipi, quindi non è possibile osservare il fatto che ci sono più riferimenti allo stesso valore.

Le strutture, d'altra parte, possono essere modificabili se le variabili di istanza sono accessibili o se i relativi metodi o proprietà modificano le variabili di istanza. Se si utilizza un riferimento a una struttura boxed per modificare la struttura, tutti i riferimenti alla struttura boxed vedranno la modifica. Poiché questo risultato potrebbe essere imprevisto, quando un valore digitato come Object viene copiato da una posizione a un altro tipo valore boxed verrà clonato automaticamente nell'heap anziché semplicemente avere copiati i relativi riferimenti. Per esempio:

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

L'output del programma è:

Values: 0, 123
Refs: 123, 123

L'assegnazione al campo della variabile locale non influisce val2 sul campo della variabile val1 locale perché quando il boxed Struct1 è stato assegnato a val2, è stata eseguita una copia del valore. Al contrario, l'assegnazione ref2.Value = 123 influisce sull'oggetto a cui ref1 fa riferimento e ref2 .

Nota. La copia della struttura non viene eseguita per le strutture boxed tipate perché System.ValueType non è possibile eseguire l'associazione tardiva di System.ValueType.

Esiste un'eccezione alla regola che i tipi valore boxed verranno copiati in fase di assegnazione. Se un riferimento al tipo di valore boxed viene archiviato all'interno di un altro tipo, il riferimento interno non verrà copiato. Per esempio:

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

L'output del programma è:

Values: 123, 123

Ciò è dovuto al fatto che il valore boxed interno non viene copiato quando il valore viene copiato. Pertanto, sia val1.Value che val2.Value hanno un riferimento allo stesso tipo di valore boxed.

Nota. Il fatto che i tipi di valore interno boxed non vengano copiati è una limitazione del sistema di tipi .NET, per garantire che tutti i tipi di valore interni boxed siano stati copiati ogni volta che un valore di tipo Object è stato copiato sarebbe eccessivamente costoso.

Come descritto in precedenza, i tipi valore boxed possono essere unboxed solo nel tipo originale. I tipi primitivi boxed, tuttavia, vengono trattati appositamente quando tipizzato come Object. Possono essere convertiti in qualsiasi altro tipo primitivo in cui dispongono di una conversione. Per esempio:

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

In genere, il valore 5 boxed Integer non può essere sottoposto a unboxing in una Byte variabile. Tuttavia, poiché Integer e Byte sono tipi primitivi e hanno una conversione, la conversione è consentita.

È importante notare che la conversione di un tipo valore in un'interfaccia è diversa da un argomento generico vincolato a un'interfaccia. Quando si accede ai membri dell'interfaccia in un parametro di tipo vincolato (o chiamando metodi in Object), il boxing non si verifica come avviene quando un tipo di valore viene convertito in un'interfaccia e si accede a un membro di interfaccia. Si supponga, ad esempio, che un'interfaccia ICounter contenga un metodo Increment che può essere usato per modificare un valore. Se ICounter viene usato come vincolo, l'implementazione del Increment metodo viene chiamata con un riferimento alla variabile Increment chiamata su, non una copia boxed:

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

La prima chiamata a Increment modifica il valore nella variabile x. Non è equivalente alla seconda chiamata a Increment, che modifica il valore in una copia incapsulata di x. Di conseguenza, l'output del programma è:

0
1
1

Conversioni di tipi valore nullable

Un tipo di T valore può eseguire la conversione in e dalla versione nullable del tipo , T?. La conversione da T? a T genera un'eccezione System.InvalidOperationException se il valore convertito è Nothing. Inoltre, T? ha una conversione in un tipo S se T ha una conversione intrinseca in S. E se S è un tipo di valore, esistono le conversioni intrinseche seguenti tra T? e S?:

  • Conversione della stessa classificazione (narrowing o widening) da T? a S?.

  • Conversione della stessa classificazione (narrowing o widening) da T a S?.

  • Conversione di tipo narrowing da S? a T.

Ad esempio, esiste una conversione intrinseca di tipo widening da Integer? a Long? perché esiste una conversione intrinseca di estensione da Integer a Long:

Dim i As Integer? = 10
Dim l As Long? = i

Quando si esegue la conversione da T? a S?, se il valore di T? è Nothing, il valore di sarà .NothingS? Quando si esegue la conversione da a o in , se il valore di T? o S? è Nothing, verrà generata un'eccezioneSystem.InvalidCastException.ST?TS?

A causa del comportamento del tipo System.Nullable(Of T)sottostante , quando viene sottoposto a boxing un tipo valore T? nullable, il risultato è un valore boxed di tipo , non un valore boxed di tipo TT?. Viceversa, quando si esegue unboxing in un tipo valore T?nullable , il valore verrà eseguito il wrapping da System.Nullable(Of T)e Nothing verrà sottoposto a unboxing in un valore Null di tipo T?. Per esempio:

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 effetto collaterale di questo comportamento è che un tipo valore T? nullable sembra implementare tutte le interfacce di T, perché la conversione di un tipo valore in un'interfaccia richiede che il tipo venga sottoposto a boxing. Di conseguenza, T? è convertibile in tutte le interfacce convertibili T in . È importante notare, tuttavia, che un tipo valore T? nullable non implementa effettivamente le interfacce di T ai fini del controllo o della reflection di vincoli generici. Per esempio:

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

Conversioni di stringhe

La conversione in String restituisce Char una stringa il cui primo carattere è il valore del carattere. La conversione in Char restituisce String un carattere il cui valore è il primo carattere della stringa. La conversione di una matrice di Char in String restituisce una stringa i cui caratteri sono gli elementi della matrice. La conversione String in una matrice di Char risultati in una matrice di caratteri i cui elementi sono i caratteri della stringa.

Le conversioni esatte tra String e Boolean, Byte, SByteUShort, , ShortLongIntegerUIntegerDecimalSingleULongDouble, Date, , e viceversa, esulano dall'ambito di questa specifica e dipendono dall'implementazione, ad eccezione di un dettaglio. Le conversioni di stringhe considerano sempre le impostazioni cultura correnti dell'ambiente di runtime. Di conseguenza, devono essere eseguite in fase di esecuzione.

Conversioni di tipo in ampliamento

Le conversioni con estensione non superano mai il flusso, ma possono comportare una perdita di precisione. Le conversioni seguenti sono conversioni di tipo widening:

Conversioni identity/default

  • Da un tipo a se stesso.

  • Da un tipo delegato anonimo generato per la riclassificazione di un metodo lambda a qualsiasi tipo delegato con una firma identica.

  • Dal valore letterale Nothing a un tipo.

Conversioni numeriche

  • Da Byte a UShort, ShortUInteger, Integer, ULong, Long, Decimal, , Singleo Double.

  • Da SByte a Short, Integer, Long, Decimal, Singleo Double.

  • Da UShort a UInteger, Integer, ULong, Long, Decimal, Singleo Double.

  • Da Short a Integer, LongDecimal, Single o Double.

  • Da UInteger a ULong, Long, Decimal, Singleo Double.

  • Da Integer a Long, DecimalSingle o Double.

  • Da ULong a Decimal, Singleo Double.

  • Da Long a Decimalo SingleDouble.

  • Da Decimal a Single o Double.

  • Da Single a Double.

  • Dal valore letterale 0 a un tipo enumerato. (Nota. La conversione da 0 a qualsiasi tipo enumerato si estende per semplificare i flag di test. Ad esempio, se Values è un tipo enumerato con un valore One, è possibile testare una variabile v di tipo Values dicendo (v And Values.One) = 0.)

  • Da un tipo enumerato al tipo numerico sottostante o a un tipo numerico a cui il tipo numerico sottostante ha una conversione verso un tipo più esteso.

  • Da un'espressione costante di tipo ULong, UIntegerLong, Integer, , UShort, Short, Byteo SByte a un tipo più stretto, a condizione che il valore dell'espressione costante sia compreso nell'intervallo del tipo di destinazione. (Nota. Le conversioni da UInteger o Integer a ULongSingleLong o a Single o DoubleDecimal a o a Single o Double possono causare una perdita di precisione, ma non causeranno mai una perdita di grandezza. Le altre conversioni numeriche ingrandite non perdono mai informazioni.

Conversioni di riferimento

  • Da un tipo riferimento a un tipo di base.

  • Da un tipo riferimento a un tipo di interfaccia, purché il tipo implementi l'interfaccia o un'interfaccia compatibile con varianti.

  • Da un tipo di interfaccia a Object.

  • Da un tipo di interfaccia a un tipo di interfaccia compatibile con varianti.

  • Da un tipo delegato a un tipo delegato compatibile con varianti. (Nota. Molte altre conversioni di riferimento sono implicite in queste regole. Ad esempio, i delegati anonimi sono tipi di riferimento che ereditano da System.MulticastDelegate. I tipi di matrice sono tipi riferimento che ereditano da System.Array. I tipi anonimi sono tipi di riferimento che ereditano da System.Object.

Conversioni di delegati anonimi

  • Da un tipo delegato anonimo generato per la riclassificazione di un metodo lambda a qualsiasi tipo delegato più ampio.

Conversioni di matrici

  • Da un tipo di matrice con un tipo di SSe elemento a un tipo di T matrice con un tipo di Teelemento , purché tutte le condizioni seguenti siano vere:

    • S e T differiscono solo in tipo di elemento.

    • Sia Se che Te sono tipi riferimento o sono parametri di tipo noti come tipo riferimento.

    • Esiste una conversione di un riferimento, una matrice o un parametro di tipo più ampio da Se a Te.

  • Da un tipo di matrice con un tipo S di Se elemento enumerato a un tipo di T matrice con un tipo di Teelemento , purché tutte le condizioni seguenti siano vere:

    • S e T differiscono solo in tipo di elemento.

    • Te è il tipo sottostante di Se.

  • Da un tipo di S matrice di rango 1 con un tipo di Seelemento enumerato , a System.Collections.Generic.IList(Of Te), IReadOnlyCollection(Of Te)IReadOnlyList(Of Te)ICollection(Of Te)e IEnumerable(Of Te), purché uno dei seguenti sia true:

    • Entrambi Se i tipi e Te sono tipi di riferimento o sono parametri di tipo noti come un tipo riferimento e una conversione di parametri di tipo, matrice o di tipo estesa esiste da Se a Te; o

    • Te è il tipo sottostante di Se; o

    • Te è identico a Se

Conversioni dei tipi di valore

  • Da un tipo valore a un tipo di base.

  • Da un tipo valore a un tipo di interfaccia implementato dal tipo .

Conversioni di tipi di valore nullable

  • Da un tipo T al tipo T?.

  • Da un tipo a un tipo S?T? , in cui è presente una conversione verso un tipo più esteso dal tipo T al tipo S.

  • Da un tipo a un tipo S?T , in cui è presente una conversione verso un tipo più esteso dal tipo T al tipo S.

  • Da un tipo a un tipo T? di interfaccia implementato dal tipo T .

Conversioni di stringhe

  • Da Char a String.

  • Da Char() a String.

Conversioni dei parametri di tipo

  • Da un parametro di tipo a Object.

  • Da un parametro di tipo a un vincolo di tipo di interfaccia o da qualsiasi variante di interfaccia compatibile con un vincolo di tipo di interfaccia.

  • Da un parametro di tipo a un'interfaccia implementata da un vincolo di classe.

  • Da un parametro di tipo a una variante di interfaccia compatibile con un'interfaccia implementata da un vincolo di classe.

  • Da un parametro di tipo a un vincolo di classe o da un tipo di base del vincolo di classe.

  • Da un parametro di tipo a un vincolo TTxdi parametro di tipo o qualsiasi elemento Tx ha una conversione più ampia in .

Conversioni ristrette

Le conversioni di tipo narrowing sono conversioni che non possono essere dimostrate sempre riuscite, conversioni note per perdere informazioni e conversioni in domini di tipi sufficientemente diversi da meritare una notazione ridotta. Le conversioni seguenti vengono classificate come conversioni di tipo narrowing:

Conversioni booleane

  • Da Boolean a Byte, SByteUShort, Short, UInteger, Integer, ULong, Long, , Decimal, Singleo Double.

  • Da Byte, SByte, UShort, ShortUInteger, Integer, ULongLong, , Decimal, , Singleo Double a Boolean.

Conversioni numeriche

  • Da Byte a SByte.

  • Da SByte a Byte, UShort, UIntegero ULong.

  • Da UShort a Byte, SByteo Short.

  • Da Short a Byte, SByte, UShort, UIntegero ULong.

  • Da UInteger a Byte, SByte, UShort, Shorto Integer.

  • Da Integer a Byte, SByte, UShort, Short, UIntegero ULong.

  • Da ULong a Byte, SByte, UShort, Short, UInteger, Integero Long.

  • Da Long a Byte, SByte, UShort, Short, UInteger, Integero ULong.

  • Da Decimal a Byte, SByte, UShort, Short, UInteger, Integer, ULongo Long.

  • Da Single a Byte, SByteUShort, Short, UInteger, Integer, ULong, , Longo Decimal.

  • Da Double a Byte, SByte, UShortShort, UInteger, Integer, ULong, , Long, Decimalo Single.

  • Da un tipo numerico a un tipo enumerato.

  • Da un tipo enumerato a un tipo numerico il tipo numerico sottostante ha una conversione di tipo narrowing a.

  • Da un tipo enumerato a un altro tipo enumerato.

Conversioni di riferimento

  • Da un tipo riferimento a un tipo più derivato.

  • Da un tipo di classe a un tipo di interfaccia, a condizione che il tipo di classe non implementi il tipo di interfaccia o una variante del tipo di interfaccia compatibile con esso.

  • Da un tipo di interfaccia a un tipo di classe.

  • Da un tipo di interfaccia a un altro tipo di interfaccia, purché non esista alcuna relazione di ereditarietà tra i due tipi e purché non siano compatibili con varianti.

Conversioni di delegati anonimi

  • Da un tipo delegato anonimo generato per la riclassificazione di un metodo lambda a qualsiasi tipo delegato più stretto.

Conversioni di matrici

  • Da un tipo di matrice con un tipo di SSeelemento , a un tipo di T matrice con un tipo di Teelemento , a condizione che tutte le condizioni seguenti siano vere:

    • S e T differiscono solo in tipo di elemento.
    • Sia Se che Te sono tipi riferimento o sono parametri di tipo non noti come tipi valore.
    • Esiste una conversione di un riferimento, una matrice o un parametro di tipo ristretto da Se a Te.
  • Da un tipo di matrice con un tipo di S elemento a un tipo SeT di matrice con un tipo di Teelemento enumerato , purché tutte le condizioni seguenti siano vere:

    • S e T differiscono solo in tipo di elemento.
    • Se è il tipo sottostante di Te o sono entrambi tipi enumerati diversi che condividono lo stesso tipo sottostante.
  • Da un tipo di S matrice di rango 1 con un tipo di Seelemento enumerato , a IList(Of Te), IReadOnlyList(Of Te)ICollection(Of Te), IReadOnlyCollection(Of Te) e IEnumerable(Of Te), purché sia true uno dei seguenti:

    • Entrambi Se i tipi e Te sono tipi riferimento o sono parametri di tipo noti come un tipo riferimento e una conversione di parametri di tipo, matrice o di tipo ristretta esiste da Se a Te; o
    • Se è il tipo sottostante di Teo sono entrambi tipi enumerati diversi che condividono lo stesso tipo sottostante.

Conversioni dei tipi di valore

  • Da un tipo riferimento a un tipo di valore più derivato.

  • Da un tipo di interfaccia a un tipo valore, a condizione che il tipo valore implementi il tipo di interfaccia.

Conversioni di tipi di valore nullable

  • Da un tipo a un tipo T?T.

  • Da un tipo a un tipo T?S?, in cui è presente una conversione verso un tipo più piccolo dal tipo al tipo TS.

  • Da un tipo a un tipo TS?, in cui è presente una conversione verso un tipo più piccolo dal tipo al tipo TS.

  • Da un tipo a un tipo S?T, in cui è presente una conversione dal tipo S al tipo T.

Conversioni di stringhe

  • Da String a Char.

  • Da String a Char().

  • Da String a Boolean e da Boolean a String.

  • Conversioni tra String e Byte, SByteUShort, ShortIntegerUInteger, , ULongLong, Decimal, Singleo .Double

  • Da String a Date e da Date a String.

Conversioni dei parametri di tipo

  • Da Object a un parametro di tipo.

  • Da un parametro di tipo a un tipo di interfaccia, a condizione che il parametro di tipo non sia vincolato a tale interfaccia o vincolato a una classe che implementa tale interfaccia.

  • Da un tipo di interfaccia a un parametro di tipo.

  • Da un parametro di tipo a un tipo derivato di un vincolo di classe.

  • Da un parametro di tipo a qualsiasi vincolo T di parametro Tx di tipo è stata eseguita una conversione verso un tipo più piccolo.

Conversioni dei parametri di tipo

Le conversioni dei parametri di tipo sono determinate dai vincoli, se presenti, inseriti su di essi. Un parametro T di tipo può sempre essere convertito in se stesso, in e da Objecte verso e da qualsiasi tipo di interfaccia. Si noti che se il tipo T è un tipo valore in fase di esecuzione, la conversione da T a Object o un tipo di interfaccia sarà una conversione boxing e la conversione da Object o un tipo di interfaccia in T sarà una conversione unboxing. Un parametro di tipo con un vincolo C di classe definisce conversioni aggiuntive dal parametro di tipo alle C classi di base e viceversa. Un parametro T di tipo con un vincolo Tx di parametro di tipo definisce una conversione in Tx e qualsiasi elemento Tx converte in .

Una matrice il cui tipo di elemento è un parametro di tipo con un vincolo I di interfaccia ha le stesse conversioni di matrice covariante di una matrice il cui tipo di elemento è I, a condizione che il parametro di tipo abbia anche un Class vincolo di classe o (poiché solo i tipi di elemento matrice di riferimento possono essere covarianti). Una matrice il cui tipo di elemento è un parametro di tipo con un vincolo C di classe ha le stesse conversioni di matrice covariante di una matrice il cui tipo di elemento è C.

Le regole di conversione precedenti non consentono conversioni da parametri di tipo non vincolato a tipi non di interfaccia, che possono essere sorprendenti. Questo è il motivo per evitare confusione sulla semantica di tali conversioni. Si consideri ad esempio la dichiarazione seguente:

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

Se la conversione di T in Integer fosse consentita, si potrebbe facilmente aspettarsi che X(Of Integer).F(7) restituisca 7L. Tuttavia, non lo sarebbe, perché le conversioni numeriche vengono considerate solo quando i tipi sono noti come numerici in fase di compilazione. Per rendere chiara la semantica, l'esempio precedente deve invece essere scritto:

Class X(Of T)
    Public Shared Function F(t As T) As Long
        Return CLng(CObj(t))    ' OK, conversions permitted
    End Function
End Class

conversioni User-Defined

Le conversioni intrinseche sono conversioni definite dal linguaggio ,ad esempio elencate in questa specifica, mentre le conversioni definite dall'utente vengono definite eseguendo l'overload dell'operatore CType . Quando si esegue la conversione tra tipi, se non sono applicabili conversioni intrinseche, verranno considerate le conversioni definite dall'utente. Se è presente una conversione definita dall'utente più specifica per i tipi di origine e di destinazione, verrà usata la conversione definita dall'utente. In caso contrario, viene restituito un errore in fase di compilazione. La conversione più specifica è quella il cui operando è "più vicino" al tipo di origine e il cui tipo di risultato è "più vicino" al tipo di destinazione. Quando si determina la conversione definita dall'utente da usare, verrà usata la conversione più specifica di ampliamento; se non è più specifica alcuna conversione verso un tipo di dati più esteso, verrà usata la conversione di tipo narrowing più specifica. Se non è presente una conversione più specifica di narrowing, la conversione non è definita e si verifica un errore in fase di compilazione.

Le sezioni seguenti illustrano come vengono determinate le conversioni più specifiche. Usano i termini seguenti:

Se esiste una conversione intrinseca di estensione da un tipo a un tipo BA e se non AB sono né interfacce, A viene incluso da Be BincludeA .

Il tipo più incluso in un set di tipi è quello che include tutti gli altri tipi nel set. Se nessun tipo singolo include tutti gli altri tipi, il set non include più il tipo. In termini intuitivi, il tipo più incluso è il tipo "più grande" nel set, ovvero quello in cui ognuno degli altri tipi può essere convertito tramite una conversione verso un tipo più grande.

Il tipo più incluso in un set di tipi è quello incluso in tutti gli altri tipi del set. Se nessun singolo tipo è incluso in tutti gli altri tipi, il set non include più tipi. In termini intuitivi, il tipo più incluso è il tipo "più piccolo" nel set, ovvero quello che può essere convertito in ognuno degli altri tipi tramite una conversione verso un tipo più piccolo.

Quando si raccolgono le conversioni candidate definite dall'utente per un tipo T?, vengono invece usati gli operatori di conversione definiti dall'utente definiti da T . Se il tipo convertito in è anche un tipo valore nullable, vengono lifted uno degli operatori di Tconversioni definiti dall'utente che coinvolgono solo tipi valore non nullable. Un operatore di conversione da T a S viene sollevato in modo da essere una conversione da T? a S? e viene valutato convertendo TT? in , se necessario, quindi valutando l'operatore di conversione definito dall'utente da T a S e quindi convertendo S in S?, se necessario. Se il valore convertito è Nothing, tuttavia, un operatore di conversione lifted converte direttamente in un valore tipizzato Nothing come S?. Per esempio:

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

Quando si risolvono le conversioni, gli operatori di conversione definiti dall'utente sono sempre preferiti rispetto agli operatori di conversione lifted. Per esempio:

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

In fase di esecuzione, la valutazione di una conversione definita dall'utente può comportare fino a tre passaggi:

  1. In primo luogo, il valore viene convertito dal tipo di origine al tipo di operando usando una conversione intrinseca, se necessario.

  2. Viene quindi richiamata la conversione definita dall'utente.

  3. Infine, il risultato della conversione definita dall'utente viene convertito nel tipo di destinazione usando una conversione intrinseca, se necessario.

È importante notare che la valutazione di una conversione definita dall'utente non comporta mai più di un operatore di conversione definito dall'utente.

Conversione più specifica dell'estensione

Per determinare l'operatore di conversione con estensione più definito dall'utente tra due tipi, seguire questa procedura:

  1. Innanzitutto, vengono raccolti tutti gli operatori di conversione candidati. Gli operatori di conversione candidati sono tutti gli operatori di conversione con estensione definiti dall'utente nel tipo di origine e tutti gli operatori di conversione con estensione più ampia definiti dall'utente nel tipo di destinazione.

  2. Tutti gli operatori di conversione non applicabili vengono quindi rimossi dal set. Un operatore di conversione è applicabile a un tipo di origine e a un tipo di destinazione se è presente un operatore di conversione intrinseco verso un tipo di origine verso il tipo di operando ed è presente un operatore di conversione intrinseco verso l'ampliamento dal risultato dell'operatore al tipo di destinazione. Se non sono presenti operatori di conversione applicabili, non è presente alcuna conversione più specifica per l'ampliamento.

  3. Viene quindi determinato il tipo di origine più specifico degli operatori di conversione applicabili:

    • Se uno degli operatori di conversione esegue la conversione direttamente dal tipo di origine, il tipo di origine è il tipo di origine più specifico.

    • In caso contrario, il tipo di origine più specifico è il tipo più incluso nel set combinato di tipi di origine degli operatori di conversione. Se non è possibile trovare alcun tipo più incluso, non esiste una conversione più ampia specifica.

  4. Viene quindi determinato il tipo di destinazione più specifico degli operatori di conversione applicabili:

    • Se uno degli operatori di conversione viene convertito direttamente nel tipo di destinazione, il tipo di destinazione è il tipo di destinazione più specifico.

    • In caso contrario, il tipo di destinazione più specifico è il tipo più incluso nel set combinato di tipi di destinazione degli operatori di conversione. Se non è possibile trovare alcun tipo più incluso, non esiste una conversione più ampia specifica.

  5. Quindi, se esattamente un operatore di conversione converte dal tipo di origine più specifico al tipo di destinazione più specifico, questo è l'operatore di conversione più specifico. Se esiste più di un operatore di questo tipo, non è presente alcuna conversione più specifica per l'ampliamento.

Conversione più specifica di restringimento

Per determinare l'operatore di conversione con restringimento definito dall'utente più specifico tra due tipi, seguire questa procedura:

  1. Innanzitutto, vengono raccolti tutti gli operatori di conversione candidati. Gli operatori di conversione candidati sono tutti gli operatori di conversione definiti dall'utente nel tipo di origine e tutti gli operatori di conversione definiti dall'utente nel tipo di destinazione.

  2. Tutti gli operatori di conversione non applicabili vengono quindi rimossi dal set. Un operatore di conversione è applicabile a un tipo di origine e a un tipo di destinazione se è presente un operatore di conversione intrinseco dal tipo di origine al tipo di operando e esiste un operatore di conversione intrinseco dal risultato dell'operatore al tipo di destinazione. Se non sono presenti operatori di conversione applicabili, non è presente alcuna conversione di tipo narrowing più specifica.

  3. Viene quindi determinato il tipo di origine più specifico degli operatori di conversione applicabili:

    • Se uno degli operatori di conversione esegue la conversione direttamente dal tipo di origine, il tipo di origine è il tipo di origine più specifico.

    • In caso contrario, se uno degli operatori di conversione esegue la conversione da tipi che includono il tipo di origine, il tipo di origine più specifico è il tipo più incluso nel set combinato di tipi di origine di tali operatori di conversione. Se non è possibile trovare alcun tipo più incluso, non esiste una conversione più specifica di narrowing.

    • In caso contrario, il tipo di origine più specifico è il tipo più incluso nel set combinato di tipi di origine degli operatori di conversione. Se non è possibile trovare alcun tipo più incluso, non è presente alcuna conversione più specifica di tipo narrowing.

  4. Viene quindi determinato il tipo di destinazione più specifico degli operatori di conversione applicabili:

    • Se uno degli operatori di conversione viene convertito direttamente nel tipo di destinazione, il tipo di destinazione è il tipo di destinazione più specifico.

    • In caso contrario, se uno degli operatori di conversione viene convertito in tipi inclusi nel tipo di destinazione, il tipo di destinazione più specifico è il tipo più incluso nel set combinato di tipi di origine di tali operatori di conversione. Se non è possibile trovare alcun tipo più incluso, non è presente alcuna conversione più specifica di tipo narrowing.

    • In caso contrario, il tipo di destinazione più specifico è il tipo più incluso nel set combinato di tipi di destinazione degli operatori di conversione. Se non è possibile trovare alcun tipo più incluso, non esiste una conversione più specifica di narrowing.

  5. Quindi, se esattamente un operatore di conversione converte dal tipo di origine più specifico al tipo di destinazione più specifico, questo è l'operatore di conversione più specifico. Se esiste più di un operatore di questo tipo, non esiste alcuna conversione più specifica di narrowing.

Conversioni native

Diverse conversioni vengono classificate come conversioni native perché sono supportate in modo nativo da .NET Framework. Queste conversioni sono quelle che possono essere ottimizzate tramite l'uso degli DirectCast operatori di conversione e TryCast , nonché altri comportamenti speciali. Le conversioni classificate come conversioni native sono: conversioni di identità, conversioni predefinite, conversioni di riferimento, conversioni di matrici, conversioni di tipi di valore e conversioni di parametri di tipo.

Tipo dominante

Dato un set di tipi, spesso è necessario in situazioni come l'inferenza del tipo per determinare il tipo dominante del set. Il tipo dominante di un set di tipi è determinato dalla prima rimozione di tutti i tipi a cui uno o più tipi non hanno una conversione implicita. Se non sono presenti tipi lasciati a questo punto, non esiste alcun tipo dominante. Il tipo dominante è quindi il più compreso tra i tipi rimanenti. Se sono presenti più tipi più inclusi, non esiste alcun tipo dominante.