Partilhar via


Conversões no Visual Basic

A conversão é o processo de alterar um valor de um tipo para outro. Por exemplo, um valor de tipo Integer pode ser convertido em um valor de tipo Double, ou um valor de tipo Derived pode ser convertido em um valor de tipo Base, supondo que Base e Derived são ambas classes e Derived herdas de Base. As conversões podem não exigir que o valor em si seja alterado (como no último exemplo), ou podem exigir alterações significativas na representação do valor (como no primeiro exemplo).

As conversões podem estar a aumentar ou a diminuir. Uma conversão de ampliação é uma conversão de um tipo para outro tipo cujo domínio de valor é pelo menos tão grande, se não maior, do que o domínio de valor do tipo original. A ampliação das conversões nunca deve falhar. Uma conversão de estreitamento é uma conversão de um tipo para outro tipo cujo domínio de valor é menor do que o domínio de valor do tipo original ou suficientemente não relacionado para que seja necessário ter cuidado extra ao fazer a conversão (por exemplo, ao converter de Integer para String). O estreitamento das conversões, que pode implicar perda de informações, pode falhar.

A conversão de identidade (ou seja, uma conversão de um tipo para si mesmo) e a conversão de valor padrão (ou seja, uma conversão de Nothing) são definidas para todos os tipos.

Conversões implícitas e explícitas

As conversões podem ser implícitas ou explícitas. Conversões implícitas ocorrem sem qualquer sintaxe especial. Segue-se um exemplo de conversão implícita de um Integer valor num Long valor:

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

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

Conversões explícitas, por outro lado, exigem operadores de elenco. A tentativa de fazer uma conversão explícita em um valor sem um operador cast causa um erro em tempo de compilação. O exemplo a seguir usa uma conversão explícita para converter um Long valor em um Integer valor.

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

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

O conjunto de conversões implícitas depende do ambiente de compilação e da Option Strict instrução. Se a semântica estrita estiver sendo usada, apenas conversões de ampliação podem ocorrer implicitamente. Se a semântica permissiva estiver sendo usada, todas as conversões de alargamento e estreitamento (em outras palavras, todas as conversões) podem ocorrer implicitamente.

Conversões Booleanas

Embora Boolean não seja um tipo numérico, ele tem conversões estreitas de e para os tipos numéricos como se fosse um tipo enumerado. O literal True converte para o literal 255 para Byte, 65535 para UShort, 4294967295 para UInteger, para , 18446744073709551615 e ULongpara a expressão -1 para SByte, Short, Integer, Long, Decimal, Single, e Double. O literal False converte para o literal 0. Um valor numérico zero é convertido para o literal False. Todos os outros valores numéricos são convertidos para o literal True.

Há uma conversão de estreitamento de Boolean para String, convertendo para um System.Boolean.TrueString ou System.Boolean.FalseString. Há também uma conversão de estreitamento de String para Boolean: se a string era igual a TrueString ou FalseString (na cultura atual, sem distinção entre maiúsculas e minúsculas System.InvalidCastException), então ela usa o valor apropriado, caso contrário, tenta analisar a string como um tipo numérico (em hexadecimal ou octal, se possível, caso contrário como um float) e usa as regras acima, caso contrário, lança .

Conversões numéricas

Existem conversões numéricas entre os tipos Byte, SByte, , ShortUShort, UInteger, Integer, ULong, Long, DecimalSingle e , e Doubletodos os tipos enumerados. Ao serem convertidos, os tipos enumerados são tratados como se fossem seus tipos subjacentes. Ao converter para um tipo enumerado, o valor de origem não é necessário para estar em conformidade com o conjunto de valores definidos no tipo enumerado. Por exemplo:

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

As conversões numéricas são processadas em tempo de execução da seguinte forma:

  • Para uma conversão de um tipo numérico para um tipo numérico mais amplo, o valor é simplesmente convertido para o tipo mais amplo. As conversões de UInteger, Integer, ULong, Long, ou Decimal para Single ou Double são arredondadas para o valor ou Double mais próximoSingle. Embora esta conversão possa causar uma perda de precisão, nunca causará uma perda de magnitude.

  • Para uma conversão de um tipo integral para outro tipo integral, ou de Single, Doubleou Decimal para um tipo integral, o resultado depende se a verificação de estouro de número inteiro está em:

    Se o estouro de número inteiro estiver sendo verificado:

    • Se a origem for um tipo integral, a conversão será bem-sucedida se o argumento source estiver dentro do intervalo do tipo de destino. A conversão lança uma System.OverflowException exceção se o argumento de origem estiver fora do intervalo do tipo de destino.

    • Se a fonte for Single, Doubleou Decimal, o valor de origem é arredondado para cima ou para baixo para o valor integral mais próximo, e esse valor integral torna-se o resultado da conversão. Se o valor de origem estiver igualmente próximo de dois valores integrais, o valor será arredondado para o valor que tem um número par na posição de dígito menos significativa. Se o valor integral resultante estiver fora do intervalo do tipo de destino, uma System.OverflowException exceção será lançada.

    Se o estouro de número inteiro não estiver sendo verificado:

    • Se a fonte for um tipo integral, a conversão sempre será bem-sucedida e simplesmente consistirá em descartar os bits mais significativos do valor da fonte.

    • Se a fonte for Single, Doubleou Decimal, a conversão sempre terá êxito e consiste simplesmente em arredondar o valor da fonte para o valor integral mais próximo. Se o valor de origem estiver igualmente próximo de dois valores integrais, o valor será sempre arredondado para o valor que tem um número par na posição de dígito menos significativa.

  • Para uma conversão de Double para Single, o Double valor é arredondado para o valor mais próximo Single . Se o Double valor for muito pequeno para ser representado como um Single, o resultado torna-se zero positivo ou zero negativo. Se o Double valor for muito grande para representar como um Single, o resultado torna-se infinito positivo ou infinito negativo. Se o Double valor for NaN, o resultado também NaNé .

  • Para uma conversão de ou Double para Decimal, o valor de Single origem é convertido em Decimal representação e arredondado para o número mais próximo após a 28ª casa decimal, se necessário. Se o valor de origem for muito pequeno para ser representado como um Decimal, o resultado se tornará zero. Se o valor de origem for NaN, infinito ou muito grande para representar como um Decimal, uma System.OverflowException exceção será lançada.

  • Para uma conversão de Double para Single, o Double valor é arredondado para o valor mais próximo Single . Se o Double valor for muito pequeno para ser representado como um Single, o resultado torna-se zero positivo ou zero negativo. Se o Double valor for muito grande para representar como um Single, o resultado torna-se infinito positivo ou infinito negativo. Se o Double valor for NaN, o resultado também NaNé .

Conversões de referência

Os tipos de referência podem ser convertidos num tipo de base e vice-versa. As conversões de um tipo base para um tipo mais derivado só terão êxito em tempo de execução se o valor que está sendo convertido for um valor nulo, o próprio tipo derivado ou um tipo mais derivado.

Os tipos de classe e interface podem ser convertidos de e para qualquer tipo de interface. As conversões entre um tipo e um tipo de interface só são bem-sucedidas em tempo de execução se os tipos reais envolvidos tiverem uma relação de herança ou implementação. Como um tipo de interface sempre conterá uma instância de um tipo que deriva de , um tipo de Objectinterface também pode sempre ser convertido para e de Object.

Nota. Não é um erro converter uma NotInheritable classe de e para interfaces que ele não implementa porque as classes que representam classes COM podem ter implementações de interface que não são conhecidas até o tempo de execução.

Se uma conversão de referência falhar em tempo de execução, uma System.InvalidCastException exceção será lançada.

Conversões de variância de referência

Interfaces genéricas ou delegados podem ter parâmetros de tipo de variante que permitem conversões entre variantes compatíveis do tipo. Portanto, em tempo de execução, uma conversão de um tipo de classe ou um tipo de interface para um tipo de interface que seja compatível com um tipo de interface do qual herda ou implementa terá êxito. Da mesma forma, os tipos de delegados podem ser convertidos de e para tipos de delegados compatíveis com variantes. Por exemplo, o tipo de delegado

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

permitiria uma conversão de F(Of Object, Integer) para F(Of String, Integer). Ou seja, um delegado F que leva Object pode ser usado com segurança como um delegado F que leva String. Quando o delegado é invocado, o método de destino estará esperando um objeto e uma cadeia de caracteres é um objeto.

Um delegado genérico ou tipo S(Of S1,...,Sn) de interface é dito ser variante compatível com uma interface genérica ou tipo T(Of T1,...,Tn) de delegado se:

  • S e T ambos são construídos a partir do mesmo tipo U(Of U1,...,Un)genérico.

  • Para cada parâmetro Uxde tipo:

    • Se o parâmetro type foi declarado sem variância, então Sx e Tx deve ser do mesmo tipo.

    • Se o parâmetro type foi declarado In , então deve haver uma ampliação de identidade, padrão, referência, matriz ou conversão de parâmetro de Sx tipo de para Tx.

    • Se o parâmetro type foi declarado Out , então deve haver uma ampliação de identidade, padrão, referência, matriz ou conversão de parâmetro de Tx tipo de para Sx.

Ao converter de uma classe para uma interface genérica com parâmetros de tipo variante, se a classe implementar mais de uma interface compatível com variante, a conversão será ambígua se não houver uma conversão não variante. Por exemplo:

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

Conversões de delegados anônimos

Quando uma expressão classificada como um método lambda é reclassificada como um valor em um contexto onde não há nenhum tipo de destino (por exemplo, Dim x = Function(a As Integer, b As Integer) a + b), ou onde o tipo de destino não é um tipo delegado, o tipo da expressão resultante é um tipo de delegado anônimo equivalente à assinatura do método lambda. Esse tipo de delegado anônimo tem uma conversão para qualquer tipo de delegado compatível: um tipo de delegado compatível é qualquer tipo de delegado que pode ser criado usando uma expressão de criação de delegado com o método do Invoke tipo de delegado anônimo como parâmetro. Por exemplo:

' 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

Observe que os tipos System.Delegate e System.MulticastDelegate não são considerados tipos delegados (mesmo que todos os tipos de delegados herdem deles). Além disso, observe que a conversão de um tipo de delegado anônimo para um tipo de delegado compatível não é uma conversão de referência.

Conversões de vetores

Além das conversões que são definidas em matrizes em virtude do fato de serem tipos de referência, existem várias conversões especiais para matrizes.

Para quaisquer dois tipos A e B, se ambos forem tipos de referência ou parâmetros de tipo não conhecidos por serem tipos de valor, e se A tiver uma conversão de parâmetro de referência, matriz ou tipo para B, existe uma conversão de uma matriz de tipo A para uma matriz de tipo B com a mesma classificação. Essa relação é conhecida como covariância de matriz. Covariância de matriz, em particular, significa que um elemento de uma matriz cujo tipo de elemento é B pode realmente ser um elemento de uma matriz cujo tipo de elemento é A, desde que ambos A e B sejam tipos de referência e que B tenha uma conversão de referência ou conversão de matriz para A. No exemplo a seguir, a segunda invocação de F faz com que uma System.ArrayTypeMismatchException exceção seja lançada porque o tipo de elemento real de b é String, não 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

Devido à covariância de matriz, as atribuições a elementos de matrizes de tipo de referência incluem uma verificação em tempo de execução que garante que o valor que está sendo atribuído ao elemento de matriz seja realmente de um tipo permitido.

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

Neste exemplo, a atribuição ao array(i) método in inclui implicitamente uma verificação em tempo de execução que garante que o objeto referenciado pela variável value seja ou Nothing uma instância de um tipo compatível com o tipo de elemento real da matrizarrayFill. No método Main, as duas primeiras invocações de método Fill são bem-sucedidas, mas a terceira invocação faz com que uma System.ArrayTypeMismatchException exceção seja lançada ao executar a primeira atribuição para array(i). A exceção ocorre porque um Integer não pode ser armazenado em uma String matriz.

Se um dos tipos de elemento de matriz for um parâmetro de tipo cujo tipo acaba por ser um tipo de valor em tempo de execução, uma System.InvalidCastException exceção será lançada. Por exemplo:

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

Também existem conversões entre uma matriz de um tipo enumerado e uma matriz do tipo subjacente do tipo enumerado ou uma matriz de outro tipo enumerado com o mesmo tipo subjacente, desde que as matrizes tenham a mesma classificação.

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

Neste exemplo, uma matriz de Color é convertida de e para uma matriz do Bytetipo subjacente de , Color. A conversão para uma matriz de Integer, no entanto, será um erro porque Integer não é o tipo subjacente de Color.

Uma matriz rank-1 do tipo A() também tem uma conversão de matriz para os tipos IList(Of B)de interface de coleção , , IReadOnlyList(Of B)IReadOnlyCollection(Of B)ICollection(Of B), e IEnumerable(Of B) encontrada em System.Collections.Generic, desde que uma das seguintes opções seja verdadeira:

  • A e B são ambos tipos de referência ou parâmetros de tipo que não se sabe serem tipos de valor; e A tem uma conversão de parâmetros de referência, matriz ou tipo de alargamento para B;
  • A e B são ambos os tipos enumerados do mesmo tipo subjacente;
  • um de A e B é um tipo enumerado, e o outro é o seu tipo subjacente.

Qualquer matriz do tipo A com qualquer classificação também tem uma conversão de matriz para os tipos IListde interface de coleção não genéricos e ICollectionIEnumerable encontrada em System.Collections.

É possível iterar sobre as interfaces resultantes usando For Each, ou invocando os GetEnumerator métodos diretamente. No caso de matrizes rank-1 convertidas formas genéricas ou não genéricas de IList ou ICollection, também é possível obter elementos por índice. No caso de matrizes rank-1 convertidas em formas genéricas ou não genéricas de , também é possível definir elementos por índice, sujeitos às mesmas verificações de covariância de matriz de tempo de IListexecução conforme descrito acima. O comportamento de todos os outros métodos de interface é indefinido pela especificação da linguagem VB; depende do tempo de execução subjacente.

Conversões de tipo de valor

Um valor de tipo de valor pode ser convertido em um de seus tipos de referência base ou em um tipo de interface que ele implementa por meio de um processo chamado boxing. Quando um valor de tipo de valor é encaixotado, o valor é copiado do local onde ele reside para o heap do .NET Framework. Uma referência a esse local na pilha é então retornada e pode ser armazenada em uma variável de tipo de referência. Essa referência também é chamada de instância em caixa do tipo de valor. A instância em caixa tem a mesma semântica que um tipo de referência em vez de um tipo de valor.

Os tipos de valor in a box podem ser convertidos de volta ao seu tipo de valor original por meio de um processo chamado unboxing. Quando um tipo de valor em caixa é desencaixotado, o valor é copiado da pilha para um local variável. A partir daí, ele se comporta como se fosse um tipo de valor. Ao desencaixotar um tipo de valor, o valor deve ser um valor nulo ou uma instância do tipo de valor. Caso contrário, uma System.InvalidCastException exceção é lançada. Se o valor for uma instância de um tipo enumerado, esse valor também poderá ser desencaixotado para o tipo subjacente do tipo enumerado ou outro tipo enumerado que tenha o mesmo tipo subjacente. Um valor nulo é tratado como se fosse o literal Nothing.

Para suportar bem os tipos de valor anuláveis, o tipo System.Nullable(Of T) de valor é tratado especialmente ao fazer boxe e unboxing. Boxar um valor do tipo Nullable(Of T) resulta em um valor em caixa do tipo T se a propriedade do HasValue valor for True ou um valor de se a propriedade do NothingHasValue valor for False. Desencaixotar um valor do tipo T para resulta em uma instância cuja ValueNullable(Of T) propriedade é o valor in a box e cuja HasValue propriedade é TrueNullable(Of T) . O valor Nothing pode ser desencaixotado para Nullable(Of T) qualquer T e resulta em um valor cuja HasValue propriedade é False. Como os tipos de valor em caixa se comportam como tipos de referência, é possível criar várias referências ao mesmo valor. Para os tipos primitivos e tipos enumerados, isso é irrelevante porque instâncias desses tipos são imutáveis. Ou seja, não é possível modificar uma instância em caixa desses tipos, portanto, não é possível observar o fato de que existem várias referências ao mesmo valor.

As estruturas, por outro lado, podem ser mutáveis se suas variáveis de instância estiverem acessíveis ou se seus métodos ou propriedades modificarem suas variáveis de instância. Se uma referência a uma estrutura em caixa for usada para modificar a estrutura, todas as referências à estrutura em caixa verão a alteração. Como esse resultado pode ser inesperado, quando um valor digitado como Object é copiado de um local para outro, os tipos de valor em caixa serão automaticamente clonados na pilha em vez de simplesmente terem suas referências copiadas. Por exemplo:

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

A saída do programa é:

Values: 0, 123
Refs: 123, 123

A atribuição ao campo da variável val2 local não afeta o campo da variável val1 local porque quando a caixa Struct1 foi atribuída a val2, uma cópia do valor foi feita. Em contraste, a atribuição ref2.Value = 123 afeta o objeto que ambos e ref1ref2 referencia.

Nota. A cópia de estrutura não é feita para estruturas em caixa digitadas porque System.ValueType não é possível vincular tardiamente o .System.ValueType

Há uma exceção à regra de que os tipos de valor em caixa serão copiados na atribuição. Se uma referência de tipo de valor em caixa for armazenada em outro tipo, a referência interna não será copiada. Por exemplo:

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

A saída do programa é:

Values: 123, 123

Isso ocorre porque o valor da caixa interna não é copiado quando o valor é copiado. Assim, ambos val1.Value e val2.Value têm uma referência ao mesmo tipo de valor em caixa.

Nota. O fato de que os tipos de valor em caixa interna não são copiados é uma limitação do sistema de tipos .NET -- garantir que todos os tipos de valor em caixa interna fossem copiados sempre que um valor de tipo Object fosse copiado seria proibitivamente caro.

Conforme descrito anteriormente, os tipos de valor em caixa só podem ser desencaixotados para o tipo original. Os tipos primitivos in a box, no entanto, são tratados especialmente quando digitados como Object. Eles podem ser convertidos em qualquer outro tipo primitivo para o qual tenham uma conversão. Por exemplo:

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

Normalmente, o valor 5 in Integer a box não pôde ser desencaixotado em uma Byte variável. No entanto, porque Integer e Byte são tipos primitivos e têm uma conversão, a conversão é permitida.

É importante observar que a conversão de um tipo de valor em uma interface é diferente de um argumento genérico restrito a uma interface. Ao acessar membros da interface em um parâmetro de tipo restrito (ou chamar métodos em Object), o boxe não ocorre como ocorre quando um tipo de valor é convertido em uma interface e um membro da interface é acessado. Por exemplo, suponha que uma interface ICounter contenha um método Increment que pode ser usado para modificar um valor. Se ICounter for usado como uma restrição, a Increment implementação do método será chamada com uma referência à variável que Increment foi chamada, não uma cópia em caixa:

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

A primeira chamada para Increment modifica o valor na variável x. Isso não é equivalente à segunda chamada para Increment, que modifica o valor em uma cópia encapsulada do x. Assim, a saída do programa é:

0
1
1

Conversões de tipo de valor nulo

Um tipo T de valor pode converter de e para a versão anulável do tipo, T?. A conversão de T? para T lança uma System.InvalidOperationException exceção se o valor que está sendo convertido for Nothing. Além disso, T? tem uma conversão para um tipo S se T tem uma conversão intrínseca para S. E se S é um tipo de valor, então as seguintes conversões intrínsecas existem entre T? e S?:

  • Uma conversão da mesma classificação (estreitamento ou alargamento) de T? para S?.

  • Uma conversão da mesma classificação (estreitamento ou alargamento) de T para S?.

  • Uma conversão de estreitamento de S? para T.

Por exemplo, existe uma conversão de alargamento intrínseco de Integer? para Long? porque existe uma conversão de alargamento intrínseca de Integer para Long:

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

Ao converter de T? para S?, se o valor de T? é Nothing, então o valor de S? será Nothing. Ao converter de S? para T ou T? para S, se o valor de T? ou S? for Nothing, uma System.InvalidCastException exceção será lançada.

Devido ao comportamento do tipo System.Nullable(Of T)subjacente, quando um tipo T? de valor nulo é colocado em caixa, o resultado é um valor em caixa do tipo T, não um valor em caixa do tipo T?. E, inversamente, ao desencaixotar para um tipo T?de valor anulável , o valor será encapsulado por , e Nothing será desencaixotado para System.Nullable(Of T)um valor nulo do tipo T?. Por exemplo:

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

Um efeito colateral desse comportamento é que um tipo T? de valor anulável parece implementar todas as interfaces do , porque a conversão de um tipo de Tvalor em uma interface requer que o tipo seja encaixotado. Como resultado, T? é conversível para todas as interfaces que T é conversível para. É importante notar, no entanto, que um tipo T? de valor anulável não implementa realmente as interfaces de para fins de verificação ou reflexão genérica de T restrições. Por exemplo:

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

Conversões de cadeia de caracteres

A conversão Char em String resultados em uma cadeia de caracteres cujo primeiro caractere é o valor do caractere. A conversão String em Char resultados em um caractere cujo valor é o primeiro caractere da cadeia de caracteres. A conversão de uma matriz de em String resultados em uma cadeia de Char caracteres cujos caracteres são os elementos da matriz. A conversão String em uma matriz de Char resultados em uma matriz de caracteres cujos elementos são os caracteres da cadeia de caracteres.

As conversões exatas entre e , , , , UShortShort, IntegerLongUIntegerULong, DoubleDecimalDateSinglee vice-versa, estão além do escopo desta especificação e dependem da implementação, com exceção de um detalhe. SByteByteBooleanString As conversões de cadeia de caracteres sempre consideram a cultura atual do ambiente de tempo de execução. Como tal, devem ser executados em tempo de execução.

Ampliando as conversões

O aumento das conversões nunca transborda, mas pode implicar uma perda de precisão. As conversões a seguir estão ampliando as conversões:

Conversões de identidade/padrão

  • De um tipo para si mesmo.

  • De um tipo de delegado anônimo gerado para uma reclassificação de método lambda para qualquer tipo de delegado com uma assinatura idêntica.

  • Do literal Nothing para um tipo.

Conversões numéricas

  • De Byte , UShortShort, , UInteger, Integer, ULong, LongDecimalSingleou .Double

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

  • De UShort a UInteger, Integer, ULong, Long, Decimal, Singleou Double.

  • De Short , , LongIntegerDecimal, Single ou .Double

  • De UInteger a ULong, Long, Decimal, Singleou Double.

  • De Integer , , DecimalLongSingle ou .Double

  • De ULong a Decimal, Singleou Double.

  • De Long , ou SingleDecimalDouble.

  • De Decimal a Single ou Double.

  • De Single a Double.

  • Do tipo literal 0 para um tipo enumerado. (Observação. A conversão de para qualquer tipo enumerado está aumentando para simplificar os sinalizadores de 0 teste. Por exemplo, se Values for um tipo enumerado com um valor One, você pode testar uma variável v de tipo Values dizendo (v And Values.One) = 0.)

  • De um tipo enumerado para seu tipo numérico subjacente, ou para um tipo numérico para o qual seu tipo numérico subjacente tem uma conversão de ampliação.

  • De uma expressão constante do tipo ULong, Long, , IntegerUInteger, UShort, Short, Byte, ou SByte para um tipo mais restrito, desde que o valor da expressão constante esteja dentro do intervalo do tipo de destino. (Observação. Conversões de UInteger ou Integer para Single, ou Long para Single ou DoubleULong , ou Decimal para Single ou Double podem causar uma perda de precisão, mas nunca causarão uma perda de magnitude. As outras conversões numéricas que aumentam nunca perdem nenhuma informação.)

Conversões de referência

  • De um tipo de referência para um tipo base.

  • De um tipo de referência para um tipo de interface, desde que o tipo implemente a interface ou uma interface compatível com variantes.

  • De um tipo de interface para Object.

  • De um tipo de interface para um tipo de interface compatível com variante.

  • De um tipo de delegado para um tipo de delegado compatível com variante. (Observação. Muitas outras conversões de referência estão implícitas nessas regras. Por exemplo, delegados anônimos são tipos de referência que herdam de ; tipos de System.MulticastDelegatematriz são tipos de referência que herdam de System.Array; tipos anônimos são tipos de referência que herdam de System.Object.)

Conversões de Delegados Anônimos

  • De um tipo de delegado anônimo gerado para uma reclassificação de método lambda para qualquer tipo de delegado mais amplo.

Conversões de array

  • De um tipo S de matriz com um tipo Se de elemento para um tipo T de matriz com um tipo Tede elemento , desde que todos os itens a seguir sejam verdadeiros:

    • S e T diferem apenas no tipo de elemento.

    • Ambos Se e Te são tipos de referência ou são parâmetros de tipo conhecidos por serem um tipo de referência.

    • Existe uma conversão de parâmetro de referência, matriz ou tipo de ampliação de Se para Te.

  • De um tipo S de matriz com um tipo Se de elemento enumerado para um tipo T de matriz com um tipo Tede elemento , desde que todos os itens a seguir sejam verdadeiros:

    • S e T diferem apenas no tipo de elemento.

    • Te é o tipo subjacente de Se.

  • De um tipo S de matriz de classificação 1 com um tipo Sede elemento enumerado , para System.Collections.Generic.IList(Of Te), IReadOnlyList(Of Te), ICollection(Of Te), IReadOnlyCollection(Of Te), e IEnumerable(Of Te), desde que uma das seguintes opções seja verdadeira:

    • Ambos Se e Te são tipos de referência ou são parâmetros de tipo conhecidos por serem um tipo de referência, e uma referência de ampliação, matriz ou conversão de parâmetro de tipo existe de Se para Te; ou

    • Te é o tipo subjacente de Se;

    • Te é idêntica a Se

Conversões de tipo de valor

  • De um tipo de valor para um tipo base.

  • De um tipo de valor para um tipo de interface que o tipo implementa.

Conversões de tipo de valor anulável

  • De um tipo T para o tipo T?.

  • De um tipo T? para um tipo S?, onde há uma conversão de alargamento do tipo T para o tipo S.

  • De um tipo T para um tipo S?, onde há uma conversão de alargamento do tipo T para o tipo S.

  • De um tipo T? para um tipo de interface que o tipo T implementa.

Conversões de cadeia de caracteres

  • De Char a String.

  • De Char() a String.

Conversões de parâmetros de tipo

  • De um parâmetro type para Object.

  • De um parâmetro de tipo para uma restrição de tipo de interface ou qualquer variante de interface compatível com uma restrição de tipo de interface.

  • De um parâmetro type para uma interface implementada por uma restrição de classe.

  • De um parâmetro de tipo para uma variante de interface compatível com uma interface implementada por uma restrição de classe.

  • De um parâmetro de tipo para uma restrição de classe ou um tipo base da restrição de classe.

  • De um parâmetro T de tipo para uma restrição Txde parâmetro de tipo, ou qualquer coisa Tx tem uma conversão de alargamento para.

Estreitando conversões

Conversões estreitas são conversões que não podem ser comprovadas como sempre bem-sucedidas, conversões que são conhecidas por possivelmente perder informações e conversões entre domínios de tipos suficientemente diferentes para merecer notação de estreitamento. As seguintes conversões são classificadas como conversões de estreitamento:

Conversões booleanas

  • De Boolean , ByteSByte, UShort, , Short, UIntegerInteger, ULong, Long, DecimalSingle, , ou Double.

  • De Byte, SByte, UShort, , Short, IntegerUInteger, ULong, Long, DecimalSingle, , ou Double para Boolean.

Conversões numéricas

  • De Byte a SByte.

  • De SByte a Byte, UShort, UIntegerou ULong.

  • De UShort a Byte, SByteou Short.

  • De Short a Byte, SByte, UShort, UIntegerou ULong.

  • De UInteger a Byte, SByte, UShort, Shortou Integer.

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

  • De ULong a Byte, SByte, UShort, Short, UInteger, Integerou Long.

  • De Long a Byte, SByte, UShort, Short, UInteger, Integerou ULong.

  • De Decimal a Byte, SByte, UShort, Short, UInteger, Integer, ULongou Long.

  • De Single , ByteSByte, , UShort, Short, UInteger, IntegerULongLongou .Decimal

  • De Double , ByteSByte, , UShort, Short, UIntegerInteger, ULong, LongDecimal, , ou Single.

  • De um tipo numérico para um tipo enumerado.

  • De um tipo enumerado para um tipo numérico, seu tipo numérico subjacente tem uma conversão de estreitamento para.

  • De um tipo enumerado para outro tipo enumerado.

Conversões de referência

  • De um tipo de referência para um tipo mais derivado.

  • De um tipo de classe para um tipo de interface, desde que o tipo de classe não implemente o tipo de interface ou uma variante de tipo de interface compatível com ele.

  • De um tipo de interface para um tipo de classe.

  • De um tipo de interface para outro tipo de interface, desde que não haja relação de herança entre os dois tipos e desde que eles não sejam compatíveis com variantes.

Conversões de Delegados Anônimos

  • De um tipo de delegado anônimo gerado para uma reclassificação de método lambda para qualquer tipo de delegado mais restrito.

Conversões de array

  • De um tipo S de matriz com um tipo Sede elemento , para um tipo T de matriz com um tipo Tede elemento , desde que todos os itens a seguir sejam verdadeiros:

    • S e T diferem apenas no tipo de elemento.
    • Ambos e SeTe são tipos de referência ou são parâmetros de tipo não conhecidos por serem tipos de valor.
    • Existe uma referência de estreitamento, matriz ou conversão de parâmetro de Se tipo de para Te.
  • De um tipo S de matriz com um tipo Se de elemento para um tipo T de matriz com um tipo Tede elemento enumerado, desde que todos os itens a seguir sejam verdadeiros:

    • S e T diferem apenas no tipo de elemento.
    • Se é o tipo subjacente de Te , ou ambos são tipos enumerados diferentes que compartilham o mesmo tipo subjacente.
  • De um tipo S de matriz de rank 1 com um tipo Sede elemento enumerado , para IList(Of Te), IReadOnlyList(Of Te), ICollection(Of Te)IReadOnlyCollection(Of Te) , e IEnumerable(Of Te), desde que uma das seguintes opções seja verdadeira:

    • Ambos e SeTe são tipos de referência ou são parâmetros de tipo conhecidos por serem um tipo de referência, e uma referência de estreitamento, matriz ou conversão de parâmetro de tipo existe de Se para Te; ou
    • Se é o tipo subjacente de Te, ou ambos são tipos enumerados diferentes que compartilham o mesmo tipo subjacente.

Conversões de tipo de valor

  • De um tipo de referência para um tipo de valor mais derivado.

  • De um tipo de interface para um tipo de valor, desde que o tipo de valor implemente o tipo de interface.

Conversões de tipo de valor anulável

  • De um tipo T? para um tipo T.

  • De um tipo T? para um tipo S?, onde há uma conversão de estreitamento do tipo T para o tipo S.

  • De um tipo T para um tipo S?, onde há uma conversão de estreitamento do tipo T para o tipo S.

  • De um tipo S? para um tipo T, onde há uma conversão do tipo S para o tipo T.

Conversões de cadeia de caracteres

  • De String a Char.

  • De String a Char().

  • De String e para BooleanBooleanString.

  • Conversões entre e , , , UShortShort, , UIntegerInteger, ULong, Long, , Decimal, Single, ou Double. SByteByteString

  • De String e para DateDateString.

Conversões de parâmetros de tipo

  • De para um parâmetro type Object .

  • De um parâmetro type para um tipo de interface, desde que o parâmetro type não seja restrito a essa interface ou restrito a uma classe que implemente essa interface.

  • De um tipo de interface para um parâmetro type.

  • De um parâmetro type para um tipo derivado de uma restrição de classe.

  • De um parâmetro T de tipo para qualquer coisa, uma restrição Tx de parâmetro de tipo tem uma conversão de estreitamento para.

Conversões de parâmetros de tipo

As conversões dos parâmetros de tipo são determinadas pelas restrições, se houver, colocadas sobre eles. Um parâmetro T type sempre pode ser convertido para si mesmo, para e de Object, e para e de qualquer tipo de interface. Observe que, se o tipo T for um tipo de valor em tempo de execução, a conversão de para Object ou um tipo de interface será uma conversão de boxe e a conversão de ou um tipo de Object interface para T será uma conversão de T unboxing. Um parâmetro type com uma restrição C de classe define conversões adicionais do parâmetro type para C e suas classes base, e vice-versa. Um parâmetro T type com uma restrição Tx de parâmetro type define uma conversão para Tx e qualquer coisa Tx converte para.

Uma matriz cujo tipo de elemento é um parâmetro de tipo com uma restrição I de interface tem as mesmas conversões de matriz covariante que uma matriz cujo tipo de elemento é I, desde que o parâmetro type também tenha uma restrição de Class classe ou (uma vez que apenas os tipos de elemento de matriz de referência podem ser covariantes). Uma matriz cujo tipo de elemento é um parâmetro de tipo com uma restrição C de classe tem as mesmas conversões de matriz covariante que uma matriz cujo tipo de elemento é C.

As regras de conversões acima não permitem conversões de parâmetros de tipo sem restrições para tipos sem interface, o que pode ser surpreendente. A razão para isso é evitar confusão sobre a semântica de tais conversões. Por exemplo, considere a seguinte declaração:

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 a conversão de T para Integer fosse permitida, poder-se-ia facilmente esperar que X(Of Integer).F(7) voltasse 7L. No entanto, isso não aconteceria, porque as conversões numéricas só são consideradas quando os tipos são conhecidos por serem numéricos em tempo de compilação. A fim de tornar a semântica clara, o exemplo acima deve ser escrito:

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

User-Defined Conversões

As conversões intrínsecas são conversões definidas pela linguagem (ou seja, listadas nesta especificação), enquanto as conversões definidas pelo usuário são definidas sobrecarregando o CType operador. Ao converter entre tipos, se nenhuma conversão intrínseca for aplicável, as conversões definidas pelo usuário serão consideradas. Se houver uma conversão definida pelo usuário que seja mais específica para os tipos de origem e destino, a conversão definida pelo usuário será usada. Caso contrário, um erro em tempo de compilação resulta. A conversão mais específica é aquela cujo operando é "mais próximo" do tipo de origem e cujo tipo de resultado é "mais próximo" do tipo de destino. Ao determinar qual conversão definida pelo usuário usar, a conversão de alargamento mais específica será usada; Se nenhuma conversão de alargamento for mais específica, será utilizada a conversão de estreitamento mais específica. Se não houver nenhuma conversão de estreitamento mais específica, a conversão será indefinida e ocorrerá um erro em tempo de compilação.

As seções a seguir abordam como as conversões mais específicas são determinadas. Utilizam os seguintes termos:

Se existe uma conversão de alargamento intrínseco de um tipo A para um tipo B, e se nem AB são interfaces, então A é englobado por B, e Bengloba.A

O tipo mais abrangente em um conjunto de tipos é aquele tipo que engloba todos os outros tipos no conjunto. Se nenhum tipo único engloba todos os outros tipos, então o conjunto não tem o tipo mais abrangente. Em termos intuitivos, o tipo mais abrangente é o tipo "maior" do conjunto - o único tipo para o qual cada um dos outros tipos pode ser convertido através de uma conversão alargada.

O tipo mais englobado em um conjunto de tipos é aquele tipo que é englobado por todos os outros tipos no conjunto. Se nenhum tipo único é englobado por todos os outros tipos, então o conjunto não tem nenhum tipo mais englobado. Em termos intuitivos, o tipo mais englobado é o tipo "menor" do conjunto - o único tipo que pode ser convertido para cada um dos outros tipos através de uma conversão estreita.

Ao coletar as conversões definidas pelo usuário candidatas para um tipo T?, os operadores de conversão definidos pelo usuário definidos por T são usados. Se o tipo que está sendo convertido para também for um tipo de valor anulável, qualquer um dos operadores de conversões definidos pelo usuário que envolvam apenas tipos de Tvalor não anuláveis serão suspensos. Um operador de conversão de para é levantado T para ser uma conversão de para S? e é avaliado convertendo T?T? para T, se necessário, em seguida, avaliando o operador de conversão definido pelo usuário de para S e, em seguida, convertendo TS para S?, se necessário.S Se o valor que está sendo convertido for Nothing, no entanto, um operador de conversão levantado converte diretamente em um valor digitado Nothing como S?. Por exemplo:

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

Ao resolver conversões, os operadores de conversões definidas pelo usuário são sempre preferidos em relação aos operadores de conversão levantados. Por exemplo:

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

Em tempo de execução, a avaliação de uma conversão definida pelo usuário pode envolver até três etapas:

  1. Primeiro, o valor é convertido do tipo de origem para o tipo de operando usando uma conversão intrínseca, se necessário.

  2. Em seguida, a conversão definida pelo usuário é invocada.

  3. Finalmente, o resultado da conversão definida pelo usuário é convertido para o tipo de destino usando uma conversão intrínseca, se necessário.

É importante notar que a avaliação de uma conversão definida pelo usuário nunca envolverá mais de um operador de conversão definido pelo usuário.

Conversão de alargamento mais específica

A determinação do operador de conversão de ampliação mais específico definido pelo usuário entre dois tipos é realizada usando as seguintes etapas:

  1. Em primeiro lugar, todos os operadores de conversão candidatos são recolhidos. Os operadores de conversão candidatos são todos os operadores de conversão de alargamento definidos pelo utilizador no tipo de origem e todos os operadores de conversão de alargamento definidos pelo utilizador no tipo de destino.

  2. Em seguida, todos os operadores de conversão não aplicáveis são removidos do conjunto. Um operador de conversão é aplicável a um tipo de origem e a um tipo de destino se houver um operador de conversão de alargamento intrínseco do tipo de origem para o tipo de operando e se houver um operador de conversão de alargamento intrínseco do resultado do operador para o tipo de destino. Se não existirem operadores de conversão aplicáveis, não existe uma conversão de alargamento mais específica.

  3. Em seguida, determina-se o tipo de fonte mais específico dos operadores de conversão aplicáveis:

    • Se qualquer um dos operadores de conversão converter diretamente do tipo de origem, então o tipo de fonte é o tipo de fonte mais específico.

    • Caso contrário, o tipo de fonte mais específico é o tipo mais englobado no conjunto combinado de tipos de origem dos operadores de conversão. Se nenhum tipo mais abrangente puder ser encontrado, então não haverá uma conversão de ampliação mais específica.

  4. Em seguida, determina-se o tipo de destino mais específico dos operadores de conversão aplicáveis:

    • Se qualquer um dos operadores de conversão converter diretamente para o tipo de destino, então o tipo de destino é o tipo de destino mais específico.

    • Caso contrário, o tipo de destino mais específico é o tipo mais abrangente no conjunto combinado de tipos de destino dos operadores de conversão. Se nenhum tipo mais abrangente puder ser encontrado, então não haverá uma conversão de alargamento mais específica.

  5. Então, se exatamente um operador de conversão converte do tipo de origem mais específico para o tipo de destino mais específico, então este é o operador de conversão mais específico. Se existir mais do que um operador deste tipo, não existe uma conversão de alargamento mais específica.

Conversão de estreitamento mais específica

A determinação do operador de conversão de estreitamento mais específico definido pelo usuário entre dois tipos é realizada usando as seguintes etapas:

  1. Em primeiro lugar, todos os operadores de conversão candidatos são recolhidos. Os operadores de conversão candidatos são todos os operadores de conversão definidos pelo usuário no tipo de origem e todos os operadores de conversão definidos pelo usuário no tipo de destino.

  2. Em seguida, todos os operadores de conversão não aplicáveis são removidos do conjunto. Um operador de conversão é aplicável a um tipo de origem e a um tipo de destino se houver um operador de conversão intrínseco do tipo de origem para o tipo de operando e se houver um operador de conversão intrínseco do resultado do operador para o tipo de destino. Se não existirem operadores de conversão aplicáveis, não existe uma conversão de estreitamento mais específica.

  3. Em seguida, determina-se o tipo de fonte mais específico dos operadores de conversão aplicáveis:

    • Se qualquer um dos operadores de conversão converter diretamente do tipo de origem, então o tipo de fonte é o tipo de fonte mais específico.

    • Caso contrário, se qualquer um dos operadores de conversão converter de tipos que englobam o tipo de origem, então o tipo de fonte mais específico é o tipo mais englobado no conjunto combinado de tipos de origem desses operadores de conversão. Se nenhum tipo mais abrangente puder ser encontrado, então não haverá uma conversão de estreitamento mais específica.

    • Caso contrário, o tipo de fonte mais específico é o tipo mais abrangente no conjunto combinado de tipos de origem dos operadores de conversão. Se nenhum tipo mais abrangente puder ser encontrado, então não há nenhuma conversão de estreitamento mais específica.

  4. Em seguida, determina-se o tipo de destino mais específico dos operadores de conversão aplicáveis:

    • Se qualquer um dos operadores de conversão converter diretamente para o tipo de destino, então o tipo de destino é o tipo de destino mais específico.

    • Caso contrário, se qualquer um dos operadores de conversão converter em tipos que são englobados pelo tipo de destino, então o tipo de destino mais específico é o tipo mais abrangente no conjunto combinado de tipos de origem desses operadores de conversão. Se nenhum tipo mais abrangente puder ser encontrado, então não há nenhuma conversão de estreitamento mais específica.

    • Caso contrário, o tipo de destino mais específico é o tipo mais englobado no conjunto combinado de tipos de destino dos operadores de conversão. Se nenhum tipo mais abrangente puder ser encontrado, então não haverá uma conversão de estreitamento mais específica.

  5. Então, se exatamente um operador de conversão converte do tipo de origem mais específico para o tipo de destino mais específico, então este é o operador de conversão mais específico. Se existir mais do que um operador deste tipo, não existe uma conversão de estreitamento mais específica.

Conversões nativas

Várias das conversões são classificadas como conversões nativas porque são suportadas nativamente pelo .NET Framework. Essas conversões são aquelas que podem ser otimizadas através do uso dos operadores de DirectCastTryCast conversão, bem como outros comportamentos especiais. As conversões classificadas como conversões nativas são: conversões de identidade, conversões padrão, conversões de referência, conversões de matriz, conversões de tipo de valor e conversões de parâmetro de tipo.

Tipo dominante

Dado um conjunto de tipos, muitas vezes é necessário, em situações como a inferência de tipo, determinar o tipo dominante do conjunto. O tipo dominante de um conjunto de tipos é determinado removendo primeiro quaisquer tipos para os quais um ou mais tipos não tenham uma conversão implícita. Se não houver mais tipos neste momento, não há nenhum tipo dominante. O tipo dominante é então o mais englobado dos restantes tipos. Se há mais de um tipo que é mais abrangente, então não há nenhum tipo dominante.