Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La conversión es el proceso de cambiar un valor de un tipo a otro. Por ejemplo, un valor de tipo Integer se puede convertir en un valor de tipo Doubleo un valor de tipo Derived se puede convertir en un valor de tipo Base, suponiendo que Base y Derived son clases y Derived heredan de Base. Es posible que las conversiones no requieran que el propio valor cambie (como en el último ejemplo) o que requieran cambios significativos en la representación del valor (como en el ejemplo anterior).
Las conversiones pueden ser de ampliación o de restricción. Una conversión de ampliación es una conversión de un tipo a otro tipo cuyo dominio de valor es al menos tan grande, si no es mayor, que el dominio de valor del tipo original. Las conversiones de ampliación nunca deben producir un error. Una conversión de restricción es una conversión de un tipo a otro tipo cuyo dominio de valor es menor que el dominio de valor del tipo original o se debe tener suficiente cuidado al realizar la conversión (por ejemplo, al convertir de Integer a String). Se pueden producir errores en las conversiones de restricción, lo que puede implicar la pérdida de información.
La conversión de identidad (es decir, una conversión de un tipo a sí mismo) y la conversión de valor predeterminado (es decir, una conversión de Nothing) se definen para todos los tipos.
Conversiones implícitas y explícitas
Las conversiones pueden ser implícitas o explícitas. Las conversiones implícitas se producen sin ninguna sintaxis especial. A continuación se muestra un ejemplo de conversión implícita de un Integer valor a un Long valor:
Module Test
Sub Main()
Dim intValue As Integer = 123
Dim longValue As Long = intValue
Console.WriteLine(intValue & " = " & longValue)
End Sub
End Module
Por otro lado, las conversiones explícitas requieren operadores de conversión. Si se intenta realizar una conversión explícita en un valor sin un operador de conversión, se produce un error en tiempo de compilación. En el ejemplo siguiente se usa una conversión explícita para convertir un Long valor en un 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
El conjunto de conversiones implícitas depende del entorno de compilación y de la Option Strict instrucción . Si se usa una semántica estricta, solo se pueden producir conversiones de ampliación implícitamente. Si se usa la semántica permisiva, todas las conversiones de ampliación y restricción (es decir, todas las conversiones) pueden producirse implícitamente.
Conversiones booleanas
Aunque Boolean no es un tipo numérico, tiene conversiones de restricción hacia y desde los tipos numéricos como si fuera un tipo enumerado. El literal se convierte en el literal 255True para Byte, 65535 para UShort4294967295 , para UInteger18446744073709551615 , para ULongy en la expresión -1 para SByte, Short, Integer, Long, Decimal, Singley .Double El literal False se convierte en el literal 0. Un valor numérico cero se convierte en el literal False. Todos los demás valores numéricos se convierten en el literal True.
Hay una conversión de restricción de tipo Boolean a String, que se convierte en System.Boolean.TrueString o System.Boolean.FalseString. También hay una conversión de restricción de a StringBoolean: si la cadena era igual a TrueString o FalseString (en la referencia cultural actual, sin distinción entre mayúsculas y minúsculas), usa el valor adecuado; de lo contrario, intenta analizar la cadena como un tipo numérico (en hexadecimal o octal si es posible, en caso contrario, como float) y usa las reglas anteriores; de lo contrario System.InvalidCastException, produce .
Conversiones numéricas
Existen conversiones numéricas entre los tipos Byte, , ShortIntegerUIntegerUShortSByteLongDecimalULong, Single y Doubletodos los tipos enumerados. Al convertirse, los tipos enumerados se tratan como si fueran sus tipos subyacentes. Al convertir en un tipo enumerado, no es necesario que el valor de origen se ajuste al conjunto de valores definidos en el tipo enumerado. Por ejemplo:
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
Las conversiones numéricas se procesan en tiempo de ejecución de la siguiente manera:
Para una conversión de un tipo numérico a un tipo numérico más amplio, el valor se convierte simplemente en el tipo más amplio. Las conversiones de , , , o a
SingleoDoubleDecimalse redondean al valor oDoublemás cercanoSingle.LongULongIntegerUIntegerAunque esta conversión puede provocar una pérdida de precisión, nunca provocará una pérdida de magnitud.Para una conversión de un tipo entero a otro tipo entero, o de
Single,DoubleoDecimala un tipo entero, el resultado depende de si la comprobación de desbordamiento de enteros está activada:Si se comprueba el desbordamiento entero:
Si el origen es un tipo entero, la conversión se realiza correctamente si el argumento de origen está dentro del intervalo del tipo de destino. La conversión produce una
System.OverflowExceptionexcepción si el argumento de origen está fuera del intervalo del tipo de destino.Si el origen es
Single,DoubleoDecimal, el valor de origen se redondea hacia arriba o hacia abajo hasta el valor entero más cercano y este valor entero se convierte en el resultado de la conversión. Si el valor de origen está igualmente cerca de dos valores enteros, el valor se redondea al valor que tiene un número par en la posición de dígito menos significativo. Si el valor entero resultante está fuera del intervalo del tipo de destino, se produce unaSystem.OverflowExceptionexcepción.
Si no se comprueba el desbordamiento entero:
Si el origen es un tipo entero, la conversión siempre se realiza correctamente y simplemente consiste en descartar los bits más significativos del valor de origen.
Si el origen es
Single,DoubleoDecimal, la conversión siempre se realiza correctamente y simplemente consta de redondear el valor de origen hacia el valor entero más cercano. Si el valor de origen está igualmente cerca de dos valores enteros, el valor siempre se redondea al valor que tiene un número par en la posición de dígito menos significativo.
Para una conversión de
DoubleaSingle, elDoublevalor se redondea al valor más cercanoSingle. Si elDoublevalor es demasiado pequeño para representar comoSingle, el resultado se convierte en cero positivo o negativo cero. Si elDoublevalor es demasiado grande para representar comoSingle, el resultado se convierte en infinito positivo o infinito negativo. Si elDoublevalor esNaN, el resultado tambiénNaNes .Para una conversión de
SingleoDoubleaDecimal, el valor de origen se convierte enDecimalrepresentación y se redondea al número más cercano después de la posición decimal 28 si es necesario. Si el valor de origen es demasiado pequeño para representar comoDecimal, el resultado se convierte en cero. Si el valor de origen esNaN, infinito o demasiado grande para representar como ,Decimalse produce unaSystem.OverflowExceptionexcepción.Para una conversión de
DoubleaSingle, elDoublevalor se redondea al valor más cercanoSingle. Si elDoublevalor es demasiado pequeño para representar comoSingle, el resultado se convierte en cero positivo o negativo cero. Si elDoublevalor es demasiado grande para representar comoSingle, el resultado se convierte en infinito positivo o infinito negativo. Si elDoublevalor esNaN, el resultado tambiénNaNes .
Conversiones de referencia
Los tipos de referencia se pueden convertir en un tipo base y viceversa. Las conversiones de un tipo base a un tipo más derivado solo se realizan correctamente en tiempo de ejecución si el valor que se convierte es un valor NULL, el propio tipo derivado o un tipo más derivado.
Los tipos de clase e interfaz se pueden convertir hacia y desde cualquier tipo de interfaz. Las conversiones entre un tipo y un tipo de interfaz solo se realizan correctamente en tiempo de ejecución si los tipos reales implicados tienen una relación de herencia o implementación. Dado que un tipo de interfaz siempre contendrá una instancia de un tipo que deriva de Object, un tipo de interfaz también se puede convertir a y desde Object.
Nota. No es un error convertir clases NotInheritable en interfaces a y desde interfaces que no implementa porque las clases que representan clases COM pueden tener implementaciones de interfaz que no se conocen hasta el tiempo de ejecución.
Si se produce un error en una conversión de referencia en tiempo de ejecución, se produce una System.InvalidCastException excepción.
Conversiones de varianza de referencia
Las interfaces o delegados genéricos pueden tener parámetros de tipo variant que permitan conversiones entre variantes compatibles del tipo. Por lo tanto, en tiempo de ejecución, una conversión de un tipo de clase o un tipo de interfaz a un tipo de interfaz compatible con un tipo de interfaz que hereda de o implementa se realizará correctamente. Del mismo modo, los tipos de delegado se pueden convertir a y desde tipos de delegado compatibles con variantes. Por ejemplo, el tipo de delegado
Delegate Function F(Of In A, Out R)(a As A) As R
permitiría una conversión de F(Of Object, Integer) a F(Of String, Integer). Es decir, un delegado F que toma Object puede usarse de forma segura como delegado F que toma String. Cuando se invoca el delegado, el método de destino esperará un objeto y una cadena es un objeto .
Se dice que un tipo de delegado o interfaz S(Of S1,...,Sn) genérico es compatible con una interfaz genérica o un tipo T(Of T1,...,Tn) de delegado si:
SyTse construyen a partir del mismo tipoU(Of U1,...,Un)genérico.Para cada parámetro
Uxde tipo :Si el parámetro de tipo se declaró sin varianza,
SxyTxdebe ser el mismo tipo.Si el parámetro de tipo se declaró
In, debe haber una identidad de ampliación, un valor predeterminado, una referencia, una matriz o una conversión de parámetros de tipo deSxaTx.Si el parámetro de tipo se declaró
Out, debe haber una identidad de ampliación, un valor predeterminado, una referencia, una matriz o una conversión de parámetros de tipo deTxaSx.
Al convertir de una clase a una interfaz genérica con parámetros de tipo variant, si la clase implementa más de una interfaz compatible con variantes, la conversión es ambigua si no hay una conversión no variant. Por ejemplo:
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
Conversiones de delegados anónimos
Cuando una expresión clasificada como un método lambda se vuelve a clasificar como un valor en un contexto donde no hay ningún tipo de destino (por ejemplo, Dim x = Function(a As Integer, b As Integer) a + b), o donde el tipo de destino no es un tipo delegado, el tipo de la expresión resultante es un tipo delegado anónimo equivalente a la firma del método lambda. Este tipo de delegado anónimo tiene una conversión a cualquier tipo de delegado compatible: un tipo de delegado compatible es cualquier tipo de delegado que se puede crear mediante una expresión de creación de delegados con el método del Invoke tipo de delegado anónimo como parámetro. Por ejemplo:
' 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
Tenga en cuenta que los tipos System.Delegate y System.MulticastDelegate no se consideran tipos delegados (aunque todos los tipos delegados hereden de ellos). Además, tenga en cuenta que la conversión del tipo de delegado anónimo a un tipo de delegado compatible no es una conversión de referencia.
Conversiones de matriz
Además de las conversiones definidas en matrices en virtud del hecho de que son tipos de referencia, existen varias conversiones especiales para matrices.
Para los dos tipos A y B, si son tipos de referencia o parámetros de tipo no conocidos como tipos de valor, y si A tiene una conversión de parámetro de referencia, matriz o tipo en B, existe una conversión de una matriz de tipo a una matriz de tipo AB con la misma clasificación. Esta relación se conoce como covarianza de matriz. La covarianza de matriz en particular significa que un elemento de una matriz cuyo tipo de elemento es B realmente puede ser un elemento de una matriz cuyo tipo de elemento es A, siempre que y BA sean tipos de referencia y que B tenga una conversión de referencia o conversión de matriz en A. En el ejemplo siguiente, la segunda invocación de F hace que se produzca una System.ArrayTypeMismatchException excepción porque el tipo de elemento real de b es String, no 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
Debido a la covarianza de matriz, las asignaciones a elementos de matrices de tipo de referencia incluyen una comprobación en tiempo de ejecución que garantiza que el valor que se asigna al elemento de matriz es realmente de un 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
En este ejemplo, la asignación a array(i) en el método Fill incluye implícitamente una comprobación en tiempo de ejecución que garantiza que el objeto al que hace referencia la variable value sea Nothing o una instancia de un tipo compatible con el tipo de elemento real de la matriz array. En el método Main, las dos primeras invocaciones del método Fill se realizan correctamente, pero la tercera invocación hace que se produzca una System.ArrayTypeMismatchException excepción al ejecutar la primera asignación en array(i). La excepción se produce porque Integer no se puede almacenar en una String matriz.
Si uno de los tipos de elemento de matriz es un parámetro de tipo cuyo tipo resulta ser un tipo de valor en tiempo de ejecución, se producirá una System.InvalidCastException excepción. Por ejemplo:
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
Las conversiones también existen entre una matriz de un tipo enumerado y una matriz del tipo subyacente del tipo enumerado o una matriz de otro tipo enumerado con el mismo tipo subyacente, siempre que las matrices tengan el mismo rango.
Enum Color As Byte
Red
Green
Blue
End Enum
Module Test
Sub Main()
Dim a(10) As Color
Dim b() As Integer
Dim c() As Byte
b = a ' Error: Integer is not the underlying type of Color
c = a ' OK
a = c ' OK
End Sub
End Module
En este ejemplo, una matriz de Color se convierte en y desde una matriz de Byte, el tipo subyacente de . Color La conversión a una matriz de Integer, sin embargo, será un error porque Integer no es el tipo subyacente de Color.
Una matriz rank-1 de tipo A() también tiene una conversión de matriz a los tipos IList(Of B)de interfaz de colección , IReadOnlyList(Of B), ICollection(Of B)IReadOnlyCollection(Of B) y que se encuentran en System.Collections.Generic, siempre y IEnumerable(Of B) cuando se cumpla uno de los siguientes valores:
-
AyBson tipos de referencia o parámetros de tipo que no se sabe que son tipos de valor; yAtiene una referencia de ampliación, matriz o conversión de parámetros de tipo aB; o -
AyBson tipos enumerados del mismo tipo subyacente; o - uno de
AyBes un tipo enumerado, y el otro es su tipo subyacente.
Cualquier matriz de tipo A con cualquier clasificación también tiene una conversión de matriz a los tipos IListde interfaz de colección no genéricos y ICollectionIEnumerable se encuentra en System.Collections.
Es posible recorrer en iteración las interfaces resultantes mediante For Eacho mediante la invocación de los GetEnumerator métodos directamente. En el caso de matrices rank-1 convertidas de formas genéricas o no genéricas de IList o ICollection, también es posible obtener elementos por índice. En el caso de matrices rank-1 convertidas en formas genéricas o no genéricas de IList, también es posible establecer elementos por índice, sujeto a las mismas comprobaciones de covarianza de matriz en tiempo de ejecución como se describió anteriormente. El comportamiento de todos los demás métodos de interfaz no está definido por la especificación del lenguaje VB; es hasta el tiempo de ejecución subyacente.
Conversiones de tipos de valor
Un valor de tipo de valor se puede convertir en uno de sus tipos de referencia base o en un tipo de interfaz que implementa a través de un proceso denominado boxing. Cuando se boxea un valor de tipo de valor, el valor se copia desde la ubicación donde reside en el montón de .NET Framework. A continuación, se devuelve una referencia a esta ubicación en el montón y se puede almacenar en una variable de tipo de referencia. Esta referencia también se conoce como una instancia boxed del tipo de valor. La instancia boxed tiene la misma semántica que un tipo de referencia en lugar de un tipo de valor.
Los tipos de valor boxed se pueden convertir de nuevo a su tipo de valor original a través de un proceso denominado unboxing. Cuando se une unboxed un tipo de valor boxed, el valor se copia del montón en una ubicación de variable. Desde ese punto, se comporta como si fuera un tipo de valor. Al desenboxar un tipo de valor, el valor debe ser un valor NULL o una instancia del tipo de valor. De lo contrario, se produce una System.InvalidCastException excepción. Si el valor es una instancia de un tipo enumerado, ese valor también se puede desencapsular en el tipo subyacente del tipo enumerado u otro tipo enumerado que tenga el mismo tipo subyacente. Un valor NULL se trata como si fuera el literal Nothing.
Para admitir bien los tipos de valor que aceptan valores NULL, el tipo System.Nullable(Of T) de valor se trata especialmente al realizar la conversión boxing y unboxing. La conversión boxing de un valor de tipo Nullable(Of T) da como resultado un valor boxed de tipo T si la propiedad del HasValue valor es True o un valor de Nothing si la propiedad del HasValue valor es False. Al desenboxar un valor de tipo T para Nullable(Of T) dar como resultado una instancia de Nullable(Of T) cuya Value propiedad es el valor boxed y cuya HasValue propiedad es True. El valor Nothing se puede desempacar Nullable(Of T) en para cualquiera T de los valores y da como resultado un valor cuya HasValue propiedad es False. Dado que los tipos de valor boxed se comportan como tipos de referencia, es posible crear varias referencias al mismo valor. Para los tipos primitivos y los tipos enumerados, esto es irrelevante porque las instancias de esos tipos son inmutables. Es decir, no es posible modificar una instancia boxeada de esos tipos, por lo que no es posible observar el hecho de que hay varias referencias al mismo valor.
Por otro lado, las estructuras pueden ser mutables si sus variables de instancia son accesibles o si sus métodos o propiedades modifican sus variables de instancia. Si se usa una referencia a una estructura boxed para modificar la estructura, todas las referencias a la estructura boxeada verán el cambio. Dado que este resultado puede ser inesperado, cuando se copia un valor tal como Object se copia de una ubicación a otro tipo de valor boxed se clonará automáticamente en el montón en lugar de simplemente tener sus referencias copiadas. Por ejemplo:
Class Class1
Public Value As Integer = 0
End Class
Structure Struct1
Public Value As Integer
End Structure
Module Test
Sub Main()
Dim val1 As Object = New Struct1()
Dim val2 As Object = val1
val2.Value = 123
Dim ref1 As Object = New Class1()
Dim ref2 As Object = ref1
ref2.Value = 123
Console.WriteLine("Values: " & val1.Value & ", " & val2.Value)
Console.WriteLine("Refs: " & ref1.Value & ", " & ref2.Value)
End Sub
End Module
La salida del programa es:
Values: 0, 123
Refs: 123, 123
La asignación al campo de la variable val2 local no afecta al campo de la variable val1 local porque cuando se asignó Struct1 el cuadro a val2, se realizó una copia del valor. En cambio, la asignación ref2.Value = 123 afecta al objeto que tanto como ref1ref2 hace referencia a .
Nota. La copia de estructura no se realiza para las estructuras boxing tipadas como System.ValueType porque no es posible enlazar en tiempo de ejecución de System.ValueType.
Hay una excepción a la regla que los tipos de valor boxed se copiarán en la asignación. Si se almacena una referencia de tipo de valor boxed dentro de otro tipo, no se copiará la referencia interna. Por ejemplo:
Structure Struct1
Public Value As Object
End Structure
Module Test
Sub Main()
Dim val1 As Struct1
Dim val2 As Struct1
val1.Value = New Struct1()
val1.Value.Value = 10
val2 = val1
val2.Value.Value = 123
Console.WriteLine("Values: " & val1.Value.Value & ", " & _
val2.Value.Value)
End Sub
End Module
La salida del programa es:
Values: 123, 123
Esto se debe a que el valor boxed interno no se copia cuando se copia el valor. Por lo tanto, tanto como val1.Valueval2.Value tienen una referencia al mismo tipo de valor boxed.
Nota. El hecho de que los tipos de valor boxed internos no se copien es una limitación del sistema de tipos de .NET, para asegurarse de que todos los tipos de valor con conversión boxed interna se copiaron cada vez que se copiara un valor de tipo Object sería prohibitivomente costoso.
Como se ha descrito anteriormente, los tipos de valor con conversión boxing solo se pueden desempacar en su tipo original. Sin embargo, los tipos primitivos boxed se tratan especialmente cuando se escriben como Object. Se pueden convertir a cualquier otro tipo primitivo al que tengan una conversión. Por ejemplo:
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, el valor 5 con conversión boxing Integer no se pudo desempacar en una Byte variable. Sin embargo, dado Integer que y Byte son tipos primitivos y tienen una conversión, se permite la conversión.
Es importante tener en cuenta que convertir un tipo de valor en una interfaz es diferente de un argumento genérico restringido a una interfaz. Al acceder a los miembros de la interfaz en un parámetro de tipo restringido (o llamando a métodos en Object), no se produce la conversión boxing como cuando se convierte un tipo de valor en una interfaz y se accede a un miembro de interfaz. Por ejemplo, supongamos que una interfaz ICounter contiene un método Increment que se puede usar para modificar un valor. Si ICounter se usa como restricción, se llama a la implementación del Increment método con una referencia a la variable en la que Increment se llamó, no una copia boxing:
Interface ICounter
Sub Increment()
ReadOnly Property Value() As Integer
End Interface
Structure Counter
Implements ICounter
Dim _value As Integer
Property Value() As Integer Implements ICounter.Value
Get
Return _value
End Get
End Property
Sub Increment() Implements ICounter.Increment
value += 1
End Sub
End Structure
Module Test
Sub Test(Of T As ICounter)(x As T)
Console.WriteLine(x.value)
x.Increment() ' Modify x
Console.WriteLine(x.value)
CType(x, ICounter).Increment() ' Modify boxed copy of x
Console.WriteLine(x.value)
End Sub
Sub Main()
Dim x As Counter
Test(x)
End Sub
End Module
La primera llamada a Increment modifica el valor de la variable x. Esto no equivale a la segunda llamada a Increment, que modifica el valor en una copia encapsulada de x. Por lo tanto, la salida del programa es:
0
1
1
Conversiones de tipos de valor que aceptan valores NULL
Un tipo T de valor puede convertir a y desde la versión que acepta valores NULL del tipo , T?. La conversión de a T? produce una System.InvalidOperationException excepción si el valor que se va a T convertir es Nothing. Además, T? tiene una conversión a un tipo S si T tiene una conversión intrínseca a S. Y si S es un tipo de valor, existen las siguientes conversiones intrínsecas entre T? y S?:
Conversión de la misma clasificación (restricción o ampliación) de
T?aS?.Conversión de la misma clasificación (restricción o ampliación) de
TaS?.Conversión de restricción de
S?aT.
Por ejemplo, existe una conversión de ampliación intrínseca de Integer? a Long? porque existe una conversión de ampliación intrínseca de Integer a Long:
Dim i As Integer? = 10
Dim l As Long? = i
Al convertir de T? a S?, si el valor de T? es Nothing, el valor de S? será Nothing. Al convertir de a o a , si el valor de T? o S? es Nothing, se producirá una System.InvalidCastException excepción.ST?TS?
Debido al comportamiento del tipo System.Nullable(Of T)subyacente , cuando se conversión boxe un tipo T? de valor que acepta valores NULL, el resultado es un valor boxed de tipo T, no un valor boxed de tipo T?. Por el contrario, al desencapsular unboxing a un tipo T?de valor que acepta valores NULL, el valor se encapsulará mediante System.Nullable(Of T)y Nothing se desencapsulará en un valor NULL de tipo T?. Por ejemplo:
Dim i1? As Integer = Nothing
Dim o1 As Object = i1
Console.WriteLine(o1 Is Nothing) ' Will print True
o1 = 10
i1 = CType(o1, Integer?)
Console.WriteLine(i1) ' Will print 10
Un efecto secundario de este comportamiento es que un tipo T? de valor que acepta valores NULL parece implementar todas las interfaces de T, ya que la conversión de un tipo de valor en una interfaz requiere que el tipo se boxee. Como resultado, T? se puede convertir en todas las interfaces a las que T se puede convertir. Sin embargo, es importante tener en cuenta que un tipo T? de valor que acepta valores NULL no implementa realmente las interfaces de T para los fines de comprobación o reflexión de restricciones genéricas. Por ejemplo:
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
Conversiones de cadenas
La conversión Char en String da como resultado una cadena cuyo primer carácter es el valor de carácter. La String conversión en Char da como resultado un carácter cuyo valor es el primer carácter de la cadena. Convertir una matriz de Char en String da como resultado una cadena cuyos caracteres son los elementos de la matriz. La String conversión en una matriz de Char da como resultado una matriz de caracteres cuyos elementos son los caracteres de la cadena.
Las conversiones exactas entre String y Boolean, Byte, UShortUIntegerLongIntegerShortSByteDecimalSingleULong, , Double, y Dateviceversa, están fuera del ámbito de esta especificación y son dependientes de la implementación con la excepción de un detalle. Las conversiones de cadenas siempre consideran la referencia cultural actual del entorno en tiempo de ejecución. Por lo tanto, deben realizarse en tiempo de ejecución.
conversiones de ampliación
Las conversiones de ampliación nunca se desbordan, pero pueden implicar una pérdida de precisión. Las conversiones siguientes son conversiones de ampliación:
Conversiones de identidad/predeterminadas
De un tipo a sí mismo.
A partir de un tipo de delegado anónimo generado para una reclasificación de método lambda a cualquier tipo delegado con una firma idéntica.
Del literal
Nothinga un tipo.
Conversiones numéricas
De
ByteaUShort,Short, ,UIntegerInteger,ULong,Long,DecimalSingleoDouble.De
SByteaShort,Integer,Long,Decimal,SingleoDouble.De
UShortaUInteger,Integer,ULong,Long,Decimal,SingleoDouble.De
ShortaInteger,Long,DecimalSingleoDouble.De
UIntegeraULong,Long,Decimal,SingleoDouble.De
IntegeraLong,DecimalSingleoDouble.De
ULongaDecimal,SingleoDouble.De
LongaDecimal,SingleoDouble.De
DecimalaSingleoDouble.De
SingleaDouble.Del literal
0a un tipo enumerado. (Nota. La conversión de0a cualquier tipo enumerado se amplía para simplificar las marcas de prueba. Por ejemplo, siValueses un tipo enumerado con un valorOne, podría probar una variablevde tipoValuesdiciendo(v And Values.One) = 0).De un tipo enumerado a su tipo numérico subyacente, o a un tipo numérico al que su tipo numérico subyacente tiene una conversión de ampliación.
A partir de una expresión constante de tipo
ULong, ,UShortIntegerUIntegerShortLong,ByteoSBytea un tipo más estrecho, siempre que el valor de la expresión constante esté dentro del intervalo del tipo de destino. (Nota. Las conversiones deUIntegeroIntegeraSingle,ULongoLongaSingleo aSingleoDoubleDecimalDoublepueden provocar una pérdida de precisión, pero nunca provocarán una pérdida de magnitud. Las otras conversiones numéricas de ampliación nunca pierden información).
Conversiones de referencia
De un tipo de referencia a un tipo base.
Desde un tipo de referencia a un tipo de interfaz, siempre que el tipo implemente la interfaz o una interfaz compatible con variantes.
De un tipo de interfaz a
Object.De un tipo de interfaz a un tipo de interfaz compatible variante.
De un tipo delegado a un tipo de delegado compatible con variantes. (Nota. Muchas otras conversiones de referencia están implícitas en estas reglas. Por ejemplo, los delegados anónimos son tipos de referencia que heredan de
System.MulticastDelegate; los tipos de matriz son tipos de referencia que heredan deSystem.Array; los tipos anónimos son tipos de referencia que heredan deSystem.Object.
Conversiones de delegados anónimos
- A partir de un tipo de delegado anónimo generado para una reclasificación de método lambda a cualquier tipo delegado más amplio.
Conversiones de matriz
De un tipo
Sde matriz con un tipoSede elemento a un tipoTde matriz con un tipoTede elemento , siempre que se cumplan todos los siguientes valores:SyTsolo difieren en el tipo de elemento.Ambos
SeyTeson tipos de referencia o son parámetros de tipo conocidos como un tipo de referencia.Existe una referencia de ampliación, matriz o conversión de parámetros de tipo de
SeaTe.
Desde un tipo
Sde matriz con un tipoSede elemento enumerado a un tipoTde matriz con un tipoTede elemento , siempre que se cumplan todos los siguientes valores:SyTsolo difieren en el tipo de elemento.Tees el tipo subyacente deSe.
A partir de un tipo
Sde matriz de rango 1 con un tipoSede elemento enumerado , aSystem.Collections.Generic.IList(Of Te),IReadOnlyList(Of Te),ICollection(Of Te)IReadOnlyCollection(Of Te), yIEnumerable(Of Te), se proporciona uno de los siguientes valores:TeySeson tipos de referencia o son parámetros de tipo conocidos como un tipo de referencia, y existe una conversión de parámetros de tipo, matriz o referencia de ampliación deSeaTe; oTees el tipo subyacente deSe; oTees idéntico aSe
Conversiones de tipo de valor
De un tipo de valor a un tipo base.
De un tipo de valor a un tipo de interfaz que implementa el tipo.
Conversiones de tipo de valor que aceptan valores NULL
De un tipo
Tal tipoT?.De un tipo
T?a un tipoS?, donde hay una conversión de ampliación del tipoTal tipoS.De un tipo
Ta un tipoS?, donde hay una conversión de ampliación del tipoTal tipoS.De un tipo
T?a un tipo de interfaz que implementa el tipoT.
Conversiones de cadenas
De
CharaString.De
Char()aString.
Conversiones de parámetros de tipo
De un parámetro de tipo a
Object.Desde un parámetro de tipo a una restricción de tipo de interfaz o cualquier variante de interfaz compatible con una restricción de tipo de interfaz.
De un parámetro de tipo a una interfaz implementada por una restricción de clase.
Desde un parámetro de tipo a una variante de interfaz compatible con una interfaz implementada por una restricción de clase.
Desde un parámetro de tipo a una restricción de clase o un tipo base de la restricción de clase.
Desde un parámetro
Tde tipo a una restricciónTxde parámetro de tipo, o cualquier cosaTxtiene una conversión de ampliación a .
conversiones de restricción
Las conversiones de restricción son conversiones que no se pueden demostrar que siempre se realizan correctamente, conversiones que se sabe que pueden perder información y conversiones entre dominios de tipos lo suficientemente diferentes para merecer la notación de restricción. Las conversiones siguientes se clasifican como conversiones de restricción:
Conversiones booleanas
De
BooleanaByte, ,SByte,UShortShort,UInteger,Integer,ULong,Long,Decimal,SingleoDouble.De
Byte, ,UShortSByte, ,UIntegerShort,Integer,ULongLong, ,Decimal, ,SingleoDoubleaBoolean.
Conversiones numéricas
De
ByteaSByte.De
SByteaByte,UShort,UIntegeroULong.De
UShortaByte,SByteoShort.De
ShortaByte,SByte,UShort,UIntegeroULong.De
UIntegeraByte,SByte,UShort,ShortoInteger.De
IntegeraByte,SByte,UShort,Short,UIntegeroULong.De
ULongaByte,SByte,UShort,Short,UInteger,IntegeroLong.De
LongaByte,SByte,UShort,Short,UInteger,IntegeroULong.De
DecimalaByte,SByte,UShort,Short,UInteger,Integer,ULongoLong.De
SingleaByte,SByte, ,UShortShort,UInteger,Integer,ULongLongoDecimal.De
DoubleaByte, ,SByte,UShortShort,UInteger,IntegerULongLongDecimalo .SingleDe un tipo numérico a un tipo enumerado.
De un tipo enumerado a un tipo numérico, su tipo numérico subyacente tiene una conversión de restricción a .
De un tipo enumerado a otro tipo enumerado.
Conversiones de referencia
De un tipo de referencia a un tipo más derivado.
Desde un tipo de clase a un tipo de interfaz, siempre que el tipo de clase no implemente el tipo de interfaz o una variante de tipo de interfaz compatible con él.
De un tipo de interfaz a un tipo de clase.
Desde un tipo de interfaz a otro tipo de interfaz, siempre que no haya ninguna relación de herencia entre los dos tipos y siempre que no sean compatibles con variantes.
Conversiones de delegados anónimos
- A partir de un tipo de delegado anónimo generado para una reclasificación de método lambda a cualquier tipo de delegado más estrecho.
Conversiones de matriz
De un tipo
Sde matriz con un tipoSede elemento , a un tipoTde matriz con un tipoTede elemento , siempre que se cumplan todas las siguientes condiciones:-
SyTsolo difieren en el tipo de elemento. - Ambos
SeyTeson tipos de referencia o son parámetros de tipo que no se sabe que son tipos de valor. - Existe una referencia de restricción, matriz o conversión de parámetros de tipo de
SeaTe.
-
De un tipo
Sde matriz con un tipoSede elemento a un tipoTde matriz con un tipoTede elemento enumerado, siempre que se cumplan todos los siguientes valores:-
SyTsolo difieren en el tipo de elemento. -
Sees el tipo subyacente deTeo son tipos enumerados diferentes que comparten el mismo tipo subyacente.
-
A partir de un tipo
Sde matriz de rango 1 con un tipoSede elemento enumerado , aIList(Of Te),IReadOnlyList(Of Te)ICollection(Of Te),IReadOnlyCollection(Of Te)yIEnumerable(Of Te), se proporciona uno de los siguientes valores:- Ambos
SeyTeson tipos de referencia o son parámetros de tipo conocidos como un tipo de referencia, y existe una referencia de restricción, matriz o conversión de parámetros de tipo desdeSeaTe; o -
Sees el tipo subyacente deTeo son tipos enumerados diferentes que comparten el mismo tipo subyacente.
- Ambos
Conversiones de tipos de valor
De un tipo de referencia a un tipo de valor más derivado.
De un tipo de interfaz a un tipo de valor, siempre que el tipo de valor implemente el tipo de interfaz.
Conversiones de tipo de valor que aceptan valores NULL
De un tipo
T?a un tipoT.De un tipo
T?a un tipoS?, donde hay una conversión de restricción del tipoTal tipoS.De un tipo
Ta un tipoS?, donde hay una conversión de restricción del tipoTal tipoS.De un tipo
S?a un tipoT, donde hay una conversión del tipoSal tipoT.
Conversiones de cadenas
De
StringaChar.De
StringaChar().De
StringaBooleany deBooleanaString.Conversiones entre
StringyByte, ,UShortIntegerULongShortSByteUInteger,Long, ,Decimal, o .SingleDoubleDe
StringaDatey deDateaString.
Conversiones de parámetros de tipo
De
Objecta un parámetro de tipo.Desde un parámetro de tipo a un tipo de interfaz, siempre que el parámetro de tipo no esté restringido a esa interfaz o a una clase que implemente esa interfaz.
De un tipo de interfaz a un parámetro de tipo.
De un parámetro de tipo a un tipo derivado de una restricción de clase.
De un parámetro
Tde tipo a cualquier cosa a la que una restricciónTxde parámetro de tipo tenga una conversión de restricción.
Conversiones de parámetros de tipo
Las conversiones de parámetros de tipo se determinan mediante las restricciones, si las hay, las colocan en ellos. Un parámetro T de tipo siempre se puede convertir a sí mismo, a y desde Objecty hacia y desde cualquier tipo de interfaz. Tenga en cuenta que si el tipo T es un tipo de valor en tiempo de ejecución, la conversión de T a Object o un tipo de interfaz será una conversión boxing y la conversión desde Object o un tipo de interfaz a T será una conversión unboxing. Un parámetro de tipo con una restricción C de clase define conversiones adicionales del parámetro de tipo a C y sus clases base, y viceversa. Un parámetro T de tipo con una restricción Tx de parámetro de tipo define una conversión a Tx y todo Tx lo que se convierte.
Matriz cuyo tipo de elemento es un parámetro de tipo con una restricción I de interfaz tiene las mismas conversiones de matriz covariante como una matriz cuyo tipo de elemento es I, siempre que el parámetro de tipo también tenga una Class restricción de clase o (ya que solo los tipos de elementos de matriz de referencia pueden ser covariantes). Matriz cuyo tipo de elemento es un parámetro de tipo con una restricción C de clase tiene las mismas conversiones de matriz covariante como una matriz cuyo tipo de elemento es C.
Las reglas de conversiones anteriores no permiten conversiones de parámetros de tipo sin restricciones a tipos que no sean de interfaz, lo que puede ser sorprendente. La razón de esto es evitar confusiones sobre la semántica de estas conversiones. Por ejemplo, considere la siguiente declaración:
Class X(Of T)
Public Shared Function F(t As T) As Long
Return CLng(t) ' Error, explicit conversion not permitted
End Function
End Class
Si se permitía la conversión de T a Integer , podría esperarse fácilmente que X(Of Integer).F(7) devolvería 7L. Sin embargo, no lo haría, ya que las conversiones numéricas solo se consideran cuando se sabe que los tipos son numéricos en tiempo de compilación. Para que la semántica sea clara, el ejemplo anterior debe escribirse en su lugar:
Class X(Of T)
Public Shared Function F(t As T) As Long
Return CLng(CObj(t)) ' OK, conversions permitted
End Function
End Class
conversiones de User-Defined
Las conversiones intrínsecas son conversiones definidas por el lenguaje (es decir, enumeradas en esta especificación), mientras que las conversiones definidas por el usuario se definen sobrecargando el CType operador. Al convertir entre tipos, si no se aplica ninguna conversión intrínseca, se considerarán conversiones definidas por el usuario. Si hay una conversión definida por el usuario que es más específica para los tipos de origen y destino, se usará la conversión definida por el usuario. De lo contrario, se produce un error en tiempo de compilación. La conversión más específica es la cuyo operando es "más cercano" al tipo de origen y cuyo tipo de resultado es "más cercano" al tipo de destino. Al determinar qué conversión definida por el usuario se va a usar, se usará la conversión de ampliación más específica; si no hay ninguna conversión de ampliación más específica, se usará la conversión de restricción más específica. Si no hay ninguna conversión de restricción más específica, la conversión no está definida y se produce un error en tiempo de compilación.
En las secciones siguientes se explica cómo se determinan las conversiones más específicas. Usan los siguientes términos:
Si existe una conversión de ampliación intrínseca de un tipo A a un tipo B, y si ni A son B interfaces, entonces A se abarca por By BabarcaA.
El tipo más abarcante de un conjunto de tipos es el que abarca todos los demás tipos del conjunto. Si ningún tipo único abarca todos los demás tipos, el conjunto no tiene el tipo más abarcante. En términos intuitivos, el tipo más abarcador es el tipo "más grande" del conjunto: el tipo al que se puede convertir cada uno de los otros tipos a través de una conversión de ampliación.
El tipo más abarcado de un conjunto de tipos es el que abarca todos los demás tipos del conjunto. Si ningún tipo único está abarcado por todos los demás tipos, el conjunto no tiene el tipo más abarcado. En términos intuitivos, el tipo más abarcado es el tipo "más pequeño" del conjunto: el tipo que se puede convertir a cada uno de los otros tipos a través de una conversión de restricción.
Al recopilar las conversiones definidas por el usuario candidatas para un tipo T?, se usan en su lugar los operadores de conversión definidos por T el usuario. Si el tipo al que se convierte también es un tipo de valor que acepta valores NULL, se levanta cualquiera de los operadores de Tconversiones definidos por el usuario que implican solo tipos de valor que no aceptan valores NULL. Un operador de conversión de T a S se eleva para que sea una conversión de T? a S? y se evalúa mediante la conversión TT? a , si es necesario, evaluando el operador de conversión definido por el usuario de T a S y, a continuación, convirtiendo S en S?, si es necesario. Sin embargo, si el valor que se convierte es Nothing, un operador de conversión elevado convierte directamente en un valor de Nothing tipo como S?. Por ejemplo:
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
Al resolver conversiones, los operadores de conversiones definidos por el usuario siempre se prefieren sobre los operadores de conversión elevado. Por ejemplo:
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
En tiempo de ejecución, evaluar una conversión definida por el usuario puede implicar hasta tres pasos:
En primer lugar, el valor se convierte del tipo de origen al tipo de operando mediante una conversión intrínseca, si es necesario.
A continuación, se invoca la conversión definida por el usuario.
Por último, el resultado de la conversión definida por el usuario se convierte en el tipo de destino mediante una conversión intrínseca, si es necesario.
Es importante tener en cuenta que la evaluación de una conversión definida por el usuario nunca implicará más de un operador de conversión definido por el usuario.
Conversión de ampliación más específica
La determinación del operador de conversión de ampliación más específico definido por el usuario entre dos tipos se realiza mediante los pasos siguientes:
En primer lugar, se recopilan todos los operadores de conversión candidatos. Los operadores de conversión candidata son todos los operadores de conversión de ampliación definidos por el usuario en el tipo de origen y todos los operadores de conversión de ampliación definidos por el usuario en el tipo de destino.
A continuación, todos los operadores de conversión no aplicables se quitan del conjunto. Un operador de conversión se aplica a un tipo de origen y un tipo de destino si hay un operador de conversión de ampliación intrínseco del tipo de origen al tipo de operando y hay un operador de conversión de ampliación intrínseco del resultado del operador al tipo de destino. Si no hay operadores de conversión aplicables, no hay ninguna conversión de ampliación más específica.
A continuación, se determina el tipo de origen más específico de los operadores de conversión aplicables:
Si alguno de los operadores de conversión convierte directamente desde el tipo de origen, el tipo de origen es el tipo de origen más específico.
De lo contrario, el tipo de origen más específico es el tipo más abarcado en el conjunto combinado de tipos de origen de los operadores de conversión. Si no se puede encontrar el tipo más abarcado, no hay ninguna conversión de ampliación más específica.
A continuación, se determina el tipo de destino más específico de los operadores de conversión aplicables:
Si alguno de los operadores de conversión convierte directamente al tipo de destino, el tipo de destino es el tipo de destino más específico.
De lo contrario, el tipo de destino más específico es el tipo más abarcante del conjunto combinado de tipos de destino de los operadores de conversión. Si no se puede encontrar el tipo más abarcador, no hay ninguna conversión de ampliación más específica.
A continuación, si exactamente un operador de conversión convierte del tipo de origen más específico al tipo de destino más específico, este es el operador de conversión más específico. Si existe más de un operador de este tipo, no hay ninguna conversión de ampliación más específica.
Conversión de restricción más específica
La determinación del operador de conversión de restricción más específico definido por el usuario entre dos tipos se realiza mediante los pasos siguientes:
En primer lugar, se recopilan todos los operadores de conversión candidatos. Los operadores de conversión candidatos son todos los operadores de conversión definidos por el usuario en el tipo de origen y todos los operadores de conversión definidos por el usuario en el tipo de destino.
A continuación, todos los operadores de conversión no aplicables se quitan del conjunto. Un operador de conversión es aplicable a un tipo de origen y un tipo de destino si hay un operador de conversión intrínseco del tipo de origen al tipo de operando y hay un operador de conversión intrínseco del resultado del operador al tipo de destino. Si no hay operadores de conversión aplicables, no hay ninguna conversión de restricción más específica.
A continuación, se determina el tipo de origen más específico de los operadores de conversión aplicables:
Si alguno de los operadores de conversión convierte directamente desde el tipo de origen, el tipo de origen es el tipo de origen más específico.
De lo contrario, si alguno de los operadores de conversión convierte de tipos que abarcan el tipo de origen, el tipo de origen más específico es el tipo más abarcado en el conjunto combinado de tipos de origen de esos operadores de conversión. Si no se encuentra ningún tipo más abarcado, no hay ninguna conversión de restricción más específica.
De lo contrario, el tipo de origen más específico es el tipo más abarcante del conjunto combinado de tipos de origen de los operadores de conversión. Si no se puede encontrar el tipo más abarcante, no hay ninguna conversión de restricción más específica.
A continuación, se determina el tipo de destino más específico de los operadores de conversión aplicables:
Si alguno de los operadores de conversión convierte directamente al tipo de destino, el tipo de destino es el tipo de destino más específico.
De lo contrario, si alguno de los operadores de conversión convierte a tipos que están incluidos por el tipo de destino, el tipo de destino más específico es el tipo más abarcante en el conjunto combinado de tipos de origen de esos operadores de conversión. Si no se puede encontrar el tipo más abarcante, no hay ninguna conversión de restricción más específica.
De lo contrario, el tipo de destino más específico es el tipo más abarcado en el conjunto combinado de tipos de destino de los operadores de conversión. Si no se encuentra ningún tipo más abarcado, no hay ninguna conversión de restricción más específica.
A continuación, si exactamente un operador de conversión convierte del tipo de origen más específico al tipo de destino más específico, este es el operador de conversión más específico. Si existe más de un operador de este tipo, no hay ninguna conversión de restricción más específica.
Conversiones nativas
Varias de las conversiones se clasifican como conversiones nativas porque son compatibles de forma nativa con .NET Framework. Estas conversiones son las que se pueden optimizar mediante el uso de los DirectCast operadores de conversión y TryCast , así como otros comportamientos especiales. Las conversiones clasificadas como conversiones nativas son: conversiones de identidad, conversiones predeterminadas, conversiones de referencia, conversiones de matriz, conversiones de tipos de valor y conversiones de parámetros de tipo.
Tipo dominante
Dado un conjunto de tipos, a menudo es necesario en situaciones como la inferencia de tipos para determinar el tipo dominante del conjunto. El tipo dominante de un conjunto de tipos viene determinado por quitar primero los tipos a los que uno o más tipos no tienen una conversión implícita. Si no quedan tipos en este momento, no hay ningún tipo dominante. El tipo dominante es el más abarcado de los tipos restantes. Si hay más de un tipo que está más abarcado, no hay ningún tipo dominante.
Visual Basic language spec