Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
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, ouDecimalparaSingleouDoublesão arredondadas para o valor ouDoublemais 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,DoubleouDecimalpara 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.OverflowExceptionexceção se o argumento de origem estiver fora do intervalo do tipo de destino.Se a fonte for
Single,DoubleouDecimal, 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, umaSystem.OverflowExceptionexceçã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,DoubleouDecimal, 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
DoubleparaSingle, oDoublevalor é arredondado para o valor mais próximoSingle. Se oDoublevalor for muito pequeno para ser representado como umSingle, o resultado torna-se zero positivo ou zero negativo. Se oDoublevalor for muito grande para representar como umSingle, o resultado torna-se infinito positivo ou infinito negativo. Se oDoublevalor forNaN, o resultado tambémNaNé .Para uma conversão de ou
DoubleparaDecimal, o valor deSingleorigem é convertido emDecimalrepresentaçã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 umDecimal, o resultado se tornará zero. Se o valor de origem forNaN, infinito ou muito grande para representar como umDecimal, umaSystem.OverflowExceptionexceção será lançada.Para uma conversão de
DoubleparaSingle, oDoublevalor é arredondado para o valor mais próximoSingle. Se oDoublevalor for muito pequeno para ser representado como umSingle, o resultado torna-se zero positivo ou zero negativo. Se oDoublevalor for muito grande para representar como umSingle, o resultado torna-se infinito positivo ou infinito negativo. Se oDoublevalor forNaN, o resultado tambémNaNé .
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:
SeTambos são construídos a partir do mesmo tipoU(Of U1,...,Un)genérico.Para cada parâmetro
Uxde tipo:Se o parâmetro type foi declarado sem variância, então
SxeTxdeve 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 deSxtipo de paraTx.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 deTxtipo de paraSx.
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:
-
AeBsão ambos tipos de referência ou parâmetros de tipo que não se sabe serem tipos de valor; eAtem uma conversão de parâmetros de referência, matriz ou tipo de alargamento paraB; -
AeBsão ambos os tipos enumerados do mesmo tipo subjacente; - um de
AeBé 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?paraS?.Uma conversão da mesma classificação (estreitamento ou alargamento) de
TparaS?.Uma conversão de estreitamento de
S?paraT.
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
Nothingpara um tipo.
Conversões numéricas
De
Byte,UShortShort, ,UInteger,Integer,ULong,LongDecimalSingleou .DoubleDe
SByteaShort,Integer,Long,Decimal,Single, ouDouble.De
UShortaUInteger,Integer,ULong,Long,Decimal,SingleouDouble.De
Short, ,LongIntegerDecimal,Singleou .DoubleDe
UIntegeraULong,Long,Decimal,SingleouDouble.De
Integer, ,DecimalLongSingleou .DoubleDe
ULongaDecimal,SingleouDouble.De
Long, ouSingleDecimalDouble.De
DecimalaSingleouDouble.De
SingleaDouble.Do tipo literal
0para um tipo enumerado. (Observação. A conversão de para qualquer tipo enumerado está aumentando para simplificar os sinalizadores de0teste. Por exemplo, seValuesfor um tipo enumerado com um valorOne, você pode testar uma variávelvde tipoValuesdizendo(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, ouSBytepara um tipo mais restrito, desde que o valor da expressão constante esteja dentro do intervalo do tipo de destino. (Observação. Conversões deUIntegerouIntegerparaSingle, ouLongparaSingleouDoubleULong, ouDecimalparaSingleouDoublepodem 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 deSystem.Array; tipos anônimos são tipos de referência que herdam deSystem.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
Sde matriz com um tipoSede elemento para um tipoTde matriz com um tipoTede elemento , desde que todos os itens a seguir sejam verdadeiros:SeTdiferem apenas no tipo de elemento.Ambos
SeeTesã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
SeparaTe.
De um tipo
Sde matriz com um tipoSede elemento enumerado para um tipoTde matriz com um tipoTede elemento , desde que todos os itens a seguir sejam verdadeiros:SeTdiferem apenas no tipo de elemento.Teé o tipo subjacente deSe.
De um tipo
Sde matriz de classificação 1 com um tipoSede elemento enumerado , paraSystem.Collections.Generic.IList(Of Te),IReadOnlyList(Of Te),ICollection(Of Te),IReadOnlyCollection(Of Te), eIEnumerable(Of Te), desde que uma das seguintes opções seja verdadeira:Ambos
SeeTesã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 deSeparaTe; ouTeé o tipo subjacente deSe;Teé idêntica aSe
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
Tpara o tipoT?.De um tipo
T?para um tipoS?, onde há uma conversão de alargamento do tipoTpara o tipoS.De um tipo
Tpara um tipoS?, onde há uma conversão de alargamento do tipoTpara o tipoS.De um tipo
T?para um tipo de interface que o tipoTimplementa.
Conversões de cadeia de caracteres
De
CharaString.De
Char()aString.
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
Tde tipo para uma restriçãoTxde parâmetro de tipo, ou qualquer coisaTxtem 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, , ouDouble.De
Byte,SByte,UShort, ,Short,IntegerUInteger,ULong,Long,DecimalSingle, , ouDoubleparaBoolean.
Conversões numéricas
De
ByteaSByte.De
SByteaByte,UShort,UIntegerouULong.De
UShortaByte,SByteouShort.De
ShortaByte,SByte,UShort,UIntegerouULong.De
UIntegeraByte,SByte,UShort,ShortouInteger.De
IntegeraByte,SByte,UShort,Short,UInteger, ouULong.De
ULongaByte,SByte,UShort,Short,UInteger,IntegerouLong.De
LongaByte,SByte,UShort,Short,UInteger,IntegerouULong.De
DecimalaByte,SByte,UShort,Short,UInteger,Integer,ULongouLong.De
Single,ByteSByte, ,UShort,Short,UInteger,IntegerULongLongou .DecimalDe
Double,ByteSByte, ,UShort,Short,UIntegerInteger,ULong,LongDecimal, , ouSingle.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
Sde matriz com um tipoSede elemento , para um tipoTde matriz com um tipoTede elemento , desde que todos os itens a seguir sejam verdadeiros:-
SeTdiferem apenas no tipo de elemento. - Ambos e
SeTesã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
Setipo de paraTe.
-
De um tipo
Sde matriz com um tipoSede elemento para um tipoTde matriz com um tipoTede elemento enumerado, desde que todos os itens a seguir sejam verdadeiros:-
SeTdiferem apenas no tipo de elemento. -
Seé o tipo subjacente deTe, ou ambos são tipos enumerados diferentes que compartilham o mesmo tipo subjacente.
-
De um tipo
Sde matriz de rank 1 com um tipoSede elemento enumerado , paraIList(Of Te),IReadOnlyList(Of Te),ICollection(Of Te)IReadOnlyCollection(Of Te), eIEnumerable(Of Te), desde que uma das seguintes opções seja verdadeira:- Ambos e
SeTesã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 deSeparaTe; ou -
Seé o tipo subjacente deTe, ou ambos são tipos enumerados diferentes que compartilham o mesmo tipo subjacente.
- Ambos e
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 tipoT.De um tipo
T?para um tipoS?, onde há uma conversão de estreitamento do tipoTpara o tipoS.De um tipo
Tpara um tipoS?, onde há uma conversão de estreitamento do tipoTpara o tipoS.De um tipo
S?para um tipoT, onde há uma conversão do tipoSpara o tipoT.
Conversões de cadeia de caracteres
De
StringaChar.De
StringaChar().De
Stringe paraBooleanBooleanString.Conversões entre e , , ,
UShortShort, ,UIntegerInteger,ULong,Long, ,Decimal,Single, ouDouble.SByteByteStringDe
Stringe paraDateDateString.
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
Tde tipo para qualquer coisa, uma restriçãoTxde 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:
Primeiro, o valor é convertido do tipo de origem para o tipo de operando usando uma conversão intrínseca, se necessário.
Em seguida, a conversão definida pelo usuário é invocada.
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:
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.
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.
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.
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.
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:
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.
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.
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.
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.
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.
Visual Basic language spec