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.
Una expresión es una secuencia de operadores y operandos que especifica un cálculo de un valor, o que designa una variable o una constante. En este capítulo se define la sintaxis, el orden de evaluación de operandos y operadores, y el significado de las expresiones.
Expression
: SimpleExpression
| TypeExpression
| MemberAccessExpression
| DictionaryAccessExpression
| InvocationExpression
| IndexExpression
| NewExpression
| CastExpression
| OperatorExpression
| ConditionalExpression
| LambdaExpression
| QueryExpression
| XMLLiteralExpression
| XMLMemberAccessExpression
;
Clasificaciones de expresiones
Cada expresión se clasifica como una de las siguientes:
Un valor. Todos los valores tienen un tipo asociado.
Una variable. Cada variable tiene un tipo asociado, es decir, el tipo declarado de la variable.
Espacio de nombres. Una expresión con esta clasificación solo puede aparecer como el lado izquierdo de un acceso de miembro. En cualquier otro contexto, una expresión clasificada como espacio de nombres provoca un error de compilación.
Un tipo. Una expresión con esta clasificación solo puede aparecer como el lado izquierdo de un acceso de miembro. En cualquier otro contexto, una expresión clasificada como tipo provoca un error de compilación.
Un grupo de métodos, que es un conjunto de métodos sobrecargados en el mismo nombre. Un grupo de métodos puede tener una expresión de destino asociada y una lista de argumentos de tipo asociado.
Puntero de método, que representa la ubicación de un método. Un puntero de método puede tener una expresión de destino asociada y una lista de argumentos de tipo asociado.
Un método lambda, que es un método anónimo.
Un grupo de propiedades, que es un conjunto de propiedades sobrecargadas en el mismo nombre. Un grupo de propiedades puede tener una expresión de destino asociada.
Un acceso a propiedad. Todo acceso a una propiedad tiene un tipo asociado, a saber, el tipo de la propiedad. Un acceso a propiedades puede tener una expresión de destino asociada.
Acceso enlazado en tiempo de ejecución, que representa un acceso de propiedad o método aplazado hasta el tiempo de ejecución. Un acceso enlazado en tiempo de ejecución puede tener una expresión de destino asociada y una lista de argumentos de tipo asociado. El tipo de acceso enlazado en tiempo de ejecución siempre
Objectes .Un acceso a un evento. Cada acceso a un evento tiene un tipo asociado, a saber, el tipo del evento. Un acceso a eventos puede tener una expresión de destino asociada. Un acceso a eventos puede aparecer como el primer argumento de las
RaiseEventinstrucciones ,AddHandleryRemoveHandler. En cualquier otro contexto, una expresión clasificada como acceso de evento provoca un error de compilación.Literal de matriz, que representa los valores iniciales de una matriz cuyo tipo aún no se ha determinado.
Vacío. Esto ocurre cuando la expresión es una invocación de una subrutina o una expresión de operador await sin resultado. Una expresión clasificada como void solo es válida en el contexto de una instrucción de invocación o una instrucción await.
Valor predeterminado. Solo el literal
Nothinggenera esta clasificación.
El resultado final de una expresión suele ser un valor o una variable, con las otras categorías de expresiones que funcionan como valores intermedios que solo se permiten en determinados contextos.
Tenga en cuenta que las expresiones cuyo tipo es un parámetro de tipo se pueden usar en instrucciones y expresiones que requieren que el tipo de una expresión tenga determinadas características (por ejemplo, ser un tipo de referencia, un tipo de valor, derivar de algún tipo, etc.) si las restricciones impuestas en el parámetro de tipo cumplen esas características.
Reclasificación de expresiones
Normalmente, cuando se usa una expresión en un contexto que requiere una clasificación diferente de la de la expresión, se produce un error en tiempo de compilación; por ejemplo, al intentar asignar un valor a un literal. Sin embargo, en muchos casos es posible cambiar la clasificación de una expresión a través del proceso de reclasificación.
Si la reclasificación se realiza correctamente, la reclasificación se evalúa como ampliación o restricción. A menos que se indique lo contrario, se amplían todas las reclasificaciones de esta lista.
Se pueden reclasificar los siguientes tipos de expresiones:
Una variable se puede reclasificar como un valor. El valor almacenado en la variable se captura.
Un grupo de métodos se puede reclasificar como un valor. La expresión de grupo de métodos se interpreta como una expresión de invocación con la expresión de destino asociada y la lista de parámetros de tipo, y los paréntesis vacíos (es decir,
fse interpreta comof()yf(Of Integer)se interpreta comof(Of Integer)()). Esta reclasificación puede dar lugar a que la expresión se vuelva a clasificar aún más como void.Un puntero de método se puede reclasificar como un valor. Esta reclasificación solo puede producirse en el contexto de una conversión en la que se conoce el tipo de destino. La expresión de puntero de método se interpreta como argumento para una expresión de creación de instancias de delegado del tipo adecuado con la lista de argumentos de tipo asociado. Por ejemplo:
Delegate Sub D(i As Integer) Module Test Sub F(i As Integer) End Sub Sub Main() Dim del As D ' The next two lines are equivalent. del = AddressOf F del = New D(AddressOf F) End Sub End ModuleUn método lambda se puede reclasificar como un valor. Si la reclasificación se produce en el contexto de una conversión en la que se conoce el tipo de destino, se puede producir una de las dos reclasificaciones:
Si el tipo de destino es un tipo delegado, el método lambda se interpreta como argumento para una expresión de construcción de delegado del tipo adecuado.
Si el tipo de destino es
System.Linq.Expressions.Expression(Of T)yTes un tipo de delegado, el método lambda se interpreta como si se usara en la expresión de construcción de delegado paraTy, a continuación, se convierte en un árbol de expresión.
Un método lambda asincrónico o iterador solo se puede interpretar como argumento para una expresión de construcción de delegado, si el delegado no tiene parámetros ByRef.
Si la conversión de cualquiera de los tipos de parámetros del delegado a los tipos de parámetro lambda correspondientes es una conversión de restricción, la reclasificación se juzgará como restricción; de lo contrario, se amplía.
Nota. Es posible que la traducción exacta entre métodos lambda y árboles de expresión no se corriga entre versiones del compilador y esté fuera del ámbito de esta especificación. Para Microsoft Visual Basic 11.0, todas las expresiones lambda se pueden convertir en árboles de expresión sujetos a las restricciones siguientes: (1) 1. Solo las expresiones lambda de una sola línea sin parámetros ByRef se pueden convertir en árboles de expresión. De las expresiones lambda de una sola línea
Sub, solo las instrucciones de invocación se pueden convertir en árboles de expresión. (2) Las expresiones de tipo anónimo no se pueden convertir en árboles de expresión si se usa un inicializador de campo anterior para inicializar un inicializador de campo posterior, por ejemploNew With {.a=1, .b=.a}, . (3) Las expresiones del inicializador de objeto no se pueden convertir en árboles de expresión si se usa un miembro del objeto actual que se inicializa en uno de los inicializadores de campo, por ejemploNew C1 With {.a=1, .b=.Method1()}. (4) Las expresiones de creación de matrices multidimensionales solo se pueden convertir en árboles de expresión si declaran explícitamente su tipo de elemento. (5) Las expresiones de enlace en tiempo de demora no se pueden convertir en árboles de expresión. (6) Cuando una variable o campo se pasa ByRef a una expresión de invocación, pero no tiene exactamente el mismo tipo que el parámetro ByRef, o cuando se pasa una propiedad ByRef, la semántica normal de VB es que se pasa una copia del argumento ByRef y su valor final se copia de nuevo en la variable o campo o propiedad. En los árboles de expresión, no se produce la copia de seguridad. (7) Todas estas restricciones también se aplican a las expresiones lambda anidadas.Si no se conoce el tipo de destino, el método lambda se interpreta como argumento para una expresión de creación de instancias de delegado de un tipo delegado anónimo con la misma firma del método lambda. Si se usa la semántica estricta y se omite el tipo de cualquiera de los parámetros, se produce un error en tiempo de compilación; de lo contrario,
Objectse sustituye por cualquier tipo de parámetro que falte. Por ejemplo:Module Test Sub Main() ' Type of x will be equivalent to Func(Of Object, Object, Object) Dim x = Function(a, b) a + b ' Type of y will be equivalent to Action(Of Object, Object) Dim y = Sub(a, b) Console.WriteLine(a + b) End Sub End ModuleUn grupo de propiedades se puede reclasificar como acceso a propiedades. La expresión de grupo de propiedades se interpreta como una expresión de índice con paréntesis vacíos (es decir,
fse interpreta comof()).Se puede volver a clasificar un acceso de propiedad como un valor. La expresión de acceso de propiedad se interpreta como una expresión de invocación del
Getdescriptor de acceso de la propiedad. Si la propiedad no tiene ningún captador, se produce un error en tiempo de compilación.Un acceso enlazado en tiempo de ejecución se puede reclasificar como un método enlazado en tiempo de ejecución o acceso a propiedades enlazadas en tiempo de ejecución. En una situación en la que se puede volver a clasificar un acceso enlazado en tiempo de ejecución como acceso de método y como acceso a propiedades, se prefiere la reclasificación a un acceso de propiedad.
Un acceso enlazado en tiempo de ejecución se puede reclasificar como un valor.
Un literal de matriz se puede reclasificar como un valor. El tipo del valor se determina de la siguiente manera:
Si la reclasificación se produce en el contexto de una conversión donde se conoce el tipo de destino y el tipo de destino es un tipo de matriz, el literal de matriz se vuelve a clasificar como un valor de tipo T(). Si el tipo de destino es
System.Collections.Generic.IList(Of T),IReadOnlyList(Of T),ICollection(Of T),IReadOnlyCollection(Of T)oIEnumerable(Of T)y el literal de matriz tiene un nivel de anidamiento, el literal de matriz se vuelve a clasificar como un valor de tipoT().De lo contrario, el literal de matriz se reclasifica a un valor cuyo tipo es una matriz de rango igual al nivel de anidamiento se utiliza, con el tipo de elemento determinado por el tipo dominante de los elementos del inicializador; si no se puede determinar ningún tipo dominante,
Objectse usa. Por ejemplo:' x Is GetType(Double(,,)) Dim x = { { { 1, 2.0 }, { 3, 4 } }, { { 5, 6 }, { 7, 8 } } }.GetType() ' y Is GetType(Integer()) Dim y = { 1, 2, 3 }.GetType() ' z Is GetType(Object()) Dim z = { 1, "2" }.GetType() ' Error: Inconsistent nesting Dim a = { { 10 }, { 20, 30 } }.GetType()
Nota. Hay un ligero cambio en el comportamiento entre la versión 9.0 y la versión 10.0 del idioma. Antes de la versión 10.0, los inicializadores de elementos de matriz no afectaban a la inferencia de tipos de variable local y ahora lo hacen. Por lo tanto
Dim a() = { 1, 2, 3 }, habría inferidoObject()como el tipo deaen la versión 9.0 del idioma yInteger()en la versión 10.0.A continuación, la reclasificación reinterpreta el literal de matriz como expresión de creación de matrices. Por lo tanto, los ejemplos:
Dim x As Double = { 1, 2, 3, 4 } Dim y = { "a", "b" }son equivalentes a:
Dim x As Double = New Double() { 1, 2, 3, 4 } Dim y = New String() { "a", "b" }La reclasificación se considera como restricción si alguna conversión de una expresión de elemento al tipo de elemento de matriz está estrechando; de lo contrario, se considera que se amplía.
El valor
Nothingpredeterminado se puede reclasificar como un valor. En un contexto en el que se conoce el tipo de destino, el resultado es el valor predeterminado del tipo de destino. En un contexto en el que no se conoce el tipo de destino, el resultado es un valor null de tipoObject.
No se puede volver a clasificar una expresión de espacio de nombres, una expresión de tipo, una expresión de acceso a eventos o una expresión void. Se pueden realizar varias reclasificaciones al mismo tiempo. Por ejemplo:
Module Test
Sub F(i As Integer)
End Sub
ReadOnly Property P() As Integer
Get
End Get
End Sub
Sub Main()
F(P)
End Property
End Module
En este caso, la expresión P de grupo de propiedades se vuelve a clasificar de un grupo de propiedades a un acceso a propiedad y, a continuación, se vuelve a clasificar desde un acceso de propiedad a un valor. El menor número de reclasificaciones se realiza para alcanzar una clasificación válida en el contexto.
Expresiones constantes
Una expresión constante es una expresión cuyo valor se puede evaluar completamente en tiempo de compilación.
ConstantExpression
: Expression
;
El tipo de una expresión constante puede ser Byte, SByte, UShortIntegerShortULongLongUInteger, , Char, DoubleSingleDateBooleanDecimalString, , Objecto cualquier tipo de enumeración. Las siguientes construcciones se permiten en expresiones constantes:
Literales (incluido
Nothing).Referencias a miembros de tipo constante o variables locales constantes.
Referencias a miembros de tipos de enumeración.
Subexpresiones entre paréntesis.
Las expresiones de coerción, siempre que el tipo de destino sea uno de los tipos enumerados anteriormente. Las coerciones hacia y desde
Stringson una excepción a esta regla y solo se permiten en valores NULL porqueStringlas conversiones siempre se realizan en la referencia cultural actual del entorno de ejecución en tiempo de ejecución. Tenga en cuenta que las expresiones de coerción constantes solo pueden usar conversiones intrínsecas.Los
+operadores ,-yNotunarios, siempre que el operando y el resultado sean de un tipo enumerado anteriormente.Los
+operadores binarios ,-,/<*^And&Or>>Xor<<AndAlsoOrElse\Mod><>=<=y=>, siempre que cada operando y resultado sean de un tipo enumerado anteriormente.Operador condicional If, siempre que cada operando y resultado sea de un tipo enumerado anteriormente.
Las siguientes funciones en tiempo de ejecución:
Microsoft.VisualBasic.Strings.ChrW;Microsoft.VisualBasic.Strings.Chrsi el valor constante está comprendido entre 0 y 128;Microsoft.VisualBasic.Strings.AscWsi la cadena constante no está vacía;Microsoft.VisualBasic.Strings.Ascsi la cadena constante no está vacía.
Las siguientes construcciones no se permiten en expresiones constantes:
- Enlace implícito a través de un
Withcontexto.
Las expresiones constantes de un tipo entero (ULong, Long, IntegerShortUShortUInteger, , SByteo Byte) se pueden convertir implícitamente en un tipo entero más estrecho y las expresiones constantes de tipo Double se pueden convertir implícitamente en Single, siempre que el valor de la expresión constante esté dentro del intervalo del tipo de destino. Estas conversiones de restricción se permiten independientemente de si se usan semánticas permisivas o estrictas.
expresiones de Late-Bound
Cuando el destino de una expresión de acceso de miembro o expresión de índice es de tipo Object, el procesamiento de la expresión se puede aplazar hasta el tiempo de ejecución. Aplazar el procesamiento de esta manera se denomina enlace en tiempo de espera. El enlace en tiempo de ejecución permite Object usar variables de forma sin tipo , donde toda la resolución de miembros se basa en el tipo en tiempo de ejecución real del valor de la variable. Si el entorno de compilación especifica una semántica estricta o Option Strict, el enlace en tiempo de ejecución produce un error en tiempo de compilación. Los miembros que no son públicos se omiten al realizar el enlace en tiempo de espera, incluidos los fines de la resolución de sobrecargas. Tenga en cuenta que, a diferencia del caso enlazado en tiempo de ejecución, invocar o acceder a un Shared miembro enlazado en tiempo de ejecución hará que el destino de invocación se evalúe en tiempo de ejecución. Si la expresión es una expresión de invocación para un miembro definido en System.Object, el enlace en tiempo de ejecución no tendrá lugar.
En general, los accesos enlazados a tiempo de ejecución se resuelven buscando el identificador en el tipo real de tiempo de ejecución de la expresión. Si se produce un error en la búsqueda de miembros enlazados en tiempo de ejecución, se produce una System.MissingMemberException excepción. Dado que la búsqueda de miembros enlazados en tiempo de ejecución se realiza únicamente fuera del tipo en tiempo de ejecución de la expresión de destino asociada, el tipo en tiempo de ejecución de un objeto nunca es una interfaz. Por lo tanto, es imposible tener acceso a los miembros de la interfaz en una expresión de acceso de miembro enlazada en tiempo de ejecución.
Los argumentos para un acceso de miembro enlazado en tiempo de ejecución se evalúan en el orden en que aparecen en la expresión de acceso de miembro: no en el orden en que los parámetros se declaran en el miembro enlazado en tiempo de ejecución. En el ejemplo siguiente se muestra esta diferencia:
Class C
Public Sub f(ByVal x As Integer, ByVal y As Integer)
End Sub
End Class
Module Module1
Sub Main()
Console.Write("Early-bound: ")
Dim c As C = New C
c.f(y:=t("y"), x:=t("x"))
Console.Write(vbCrLf & "Late-bound: ")
Dim o As Object = New C
o.f(y:=t("y"), x:=t("x"))
End Sub
Function t(ByVal s As String) As Integer
Console.Write(s)
Return 0
End Function
End Module
Este código muestra:
Early-bound: xy
Late-bound: yx
Dado que la resolución de sobrecarga enlazada en tiempo de ejecución se realiza en el tipo de tiempo de ejecución de los argumentos, es posible que una expresión genere resultados diferentes en función de si se evalúa en tiempo de compilación o en tiempo de ejecución. En el ejemplo siguiente se muestra esta diferencia:
Class Base
End Class
Class Derived
Inherits Base
End Class
Module Test
Sub F(b As Base)
Console.WriteLine("F(Base)")
End Sub
Sub F(d As Derived)
Console.WriteLine("F(Derived)")
End Sub
Sub Main()
Dim b As Base = New Derived()
Dim o As Object = b
F(b)
F(o)
End Sub
End Module
Este código muestra:
F(Base)
F(Derived)
Expresiones simples
Las expresiones simples son literales, expresiones entre paréntesis, expresiones de instancia o expresiones de nombre simples.
SimpleExpression
: LiteralExpression
| ParenthesizedExpression
| InstanceExpression
| SimpleNameExpression
| AddressOfExpression
;
Expresiones literales
Las expresiones literales se evalúan como el valor representado por el literal. Una expresión literal se clasifica como un valor, excepto para el literal Nothing, que se clasifica como un valor predeterminado.
LiteralExpression
: Literal
;
Expresiones entre paréntesis
Una expresión entre paréntesis consta de una expresión entre paréntesis. Una expresión entre paréntesis se clasifica como un valor y la expresión entre paréntesis debe clasificarse como un valor. Una expresión entre paréntesis se evalúa como el valor de la expresión entre paréntesis.
ParenthesizedExpression
: OpenParenthesis Expression CloseParenthesis
;
Expresiones de instancia
Una expresión de instancia es la palabra clave Me. Solo se puede usar dentro del cuerpo de un método, constructor o descriptor de acceso de propiedad no compartidos. Se clasifica como un valor. La palabra clave Me representa la instancia del tipo que contiene el descriptor de acceso de método o propiedad que se está ejecutando. Si un constructor invoca explícitamente otro constructor ( Constructores de sección), Me no se puede usar hasta después de esa llamada al constructor, ya que la instancia aún no se ha construido.
InstanceExpression
: 'Me'
;
Expresiones de nombre simples
Una expresión de nombre simple consta de un único identificador seguido de una lista de argumentos de tipo opcional.
SimpleNameExpression
: Identifier ( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
;
El nombre se resuelve y clasifica mediante las siguientes "reglas de resolución de nombres simples":
A partir del bloque envolvente inmediatamente y continuar con cada bloque externo envolvente (si existe), si el identificador coincide con el nombre de una variable local, variable estática, local constante, parámetro de tipo de método o parámetro, el identificador hace referencia a la entidad coincidente.
Si el identificador coincide con una variable local, una variable estática o una constante local y se proporcionó una lista de argumentos de tipo, se produce un error en tiempo de compilación. Si el identificador coincide con un parámetro de tipo de método y se proporcionó una lista de argumentos de tipo, no se produce ninguna coincidencia y continúa la resolución. Si el identificador coincide con una variable local, la variable local coincide con la función implícita o
Getel descriptor de acceso devuelven la variable local y la expresión forma parte de una expresión de invocación, instrucción de invocación o expresiónAddressOf, no se produce ninguna coincidencia y continúa la resolución.La expresión se clasifica como una variable si es una variable local, una variable estática o un parámetro. La expresión se clasifica como un tipo si es un parámetro de tipo de método. La expresión se clasifica como un valor si es una constante local.
Para cada tipo anidado que contiene la expresión, a partir del más interno yendo al exterior, si una búsqueda del identificador del tipo genera una coincidencia con un miembro accesible:
- Si el miembro de tipo coincidente es un parámetro de tipo, el resultado se clasifica como un tipo y es el parámetro de tipo coincidente. Si se proporcionó una lista de argumentos de tipo, no se produce ninguna coincidencia y continúa la resolución.
- De lo contrario, si el tipo es el tipo envolvente inmediatamente y la búsqueda identifica un miembro de tipo no compartido, el resultado es el mismo que un acceso de miembro del formulario
Me.E(Of A), dondeEes el identificador yAes la lista de argumentos de tipo, si existe. - De lo contrario, el resultado es exactamente el mismo que un acceso de miembro del formulario
T.E(Of A), dondeTes el tipo que contiene el miembro coincidente,Ees el identificador yAes la lista de argumentos de tipo, si existe. En este caso, es un error para que el identificador haga referencia a un miembro no compartido.
Para cada espacio de nombres anidado, a partir del más interno yendo al espacio de nombres más externo, haga lo siguiente:
- Si el espacio de nombres contiene un tipo accesible con el nombre especificado y tiene el mismo número de parámetros de tipo que se proporcionó en la lista de argumentos de tipo, si existe, el identificador hace referencia a ese tipo y se clasifica como un tipo.
- De lo contrario, si no se proporcionó ninguna lista de argumentos de tipo y el espacio de nombres contiene un miembro de espacio de nombres con el nombre especificado, el identificador hace referencia a ese espacio de nombres y se clasifica como un espacio de nombres.
- De lo contrario, si el espacio de nombres contiene uno o varios módulos estándar accesibles y una búsqueda de nombre de miembro del identificador genera una coincidencia accesible en exactamente un módulo estándar, el resultado es exactamente el mismo que un acceso de miembro del formulario
M.E(Of A), dondeMes el módulo estándar que contiene el miembro coincidente,Ees el identificador yAes la lista de argumentos de tipo, si existe. Si el identificador coincide con los miembros de tipo accesibles en más de un módulo estándar, se produce un error en tiempo de compilación.
Si el archivo de origen tiene uno o más alias de importación y el identificador coincide con el nombre de uno de ellos, el identificador hace referencia a ese espacio de nombres o tipo. Si se proporciona una lista de argumentos de tipo, se produce un error en tiempo de compilación.
Si el archivo de origen que contiene la referencia de nombre tiene una o varias importaciones:
- Si el identificador coincide exactamente en una importación, el nombre de un tipo accesible con el mismo número de parámetros de tipo que se proporcionó en la lista de argumentos de tipo, si existe o un miembro de tipo, el identificador hace referencia a ese tipo o miembro de tipo. Si el identificador coincide en más de una importación, el nombre de un tipo accesible con el mismo número de parámetros de tipo que se proporcionó en la lista de argumentos de tipo, si existe o un miembro de tipo accesible, se produce un error en tiempo de compilación.
- De lo contrario, si no se proporcionó ninguna lista de argumentos de tipo y el identificador coincide exactamente en una importación del nombre de un espacio de nombres con tipos accesibles, el identificador hace referencia a ese espacio de nombres. Si no se proporcionó ninguna lista de argumentos de tipo y el identificador coincide en más de una importación del nombre de un espacio de nombres con tipos accesibles, se produce un error en tiempo de compilación.
- De lo contrario, si las importaciones contienen uno o varios módulos estándar accesibles, y una búsqueda de nombre de miembro del identificador genera una coincidencia accesible en exactamente un módulo estándar, el resultado es exactamente el mismo que un acceso de miembro del formulario
M.E(Of A), dondeMes el módulo estándar que contiene el miembro coincidente,Ees el identificador yAes la lista de argumentos de tipo, si existe. Si el identificador coincide con los miembros de tipo accesibles en más de un módulo estándar, se produce un error en tiempo de compilación.
Si el entorno de compilación define uno o varios alias de importación y el identificador coincide con el nombre de uno de ellos, el identificador hace referencia a ese espacio de nombres o tipo. Si se proporciona una lista de argumentos de tipo, se produce un error en tiempo de compilación.
Si el entorno de compilación define una o varias importaciones:
- Si el identificador coincide exactamente en una importación, el nombre de un tipo accesible con el mismo número de parámetros de tipo que se proporcionó en la lista de argumentos de tipo, si existe o un miembro de tipo, el identificador hace referencia a ese tipo o miembro de tipo. Si el identificador coincide en más de una importación, el nombre de un tipo accesible con el mismo número de parámetros de tipo que se proporcionó en la lista de argumentos de tipo, si existe o un miembro de tipo, se produce un error en tiempo de compilación.
- De lo contrario, si no se proporcionó ninguna lista de argumentos de tipo y el identificador coincide exactamente en una importación del nombre de un espacio de nombres con tipos accesibles, el identificador hace referencia a ese espacio de nombres. Si no se proporcionó ninguna lista de argumentos de tipo y el identificador coincide en más de una importación del nombre de un espacio de nombres con tipos accesibles, se produce un error en tiempo de compilación.
- De lo contrario, si las importaciones contienen uno o varios módulos estándar accesibles, y una búsqueda de nombre de miembro del identificador genera una coincidencia accesible en exactamente un módulo estándar, el resultado es exactamente el mismo que un acceso de miembro del formulario
M.E(Of A), dondeMes el módulo estándar que contiene el miembro coincidente,Ees el identificador yAes la lista de argumentos de tipo, si existe. Si el identificador coincide con los miembros de tipo accesibles en más de un módulo estándar, se produce un error en tiempo de compilación.
De lo contrario, el nombre proporcionado por el identificador no está definido.
Una expresión de nombre simple que no está definida es un error en tiempo de compilación.
Normalmente, un nombre solo puede producirse una vez en un espacio de nombres determinado. Sin embargo, dado que los espacios de nombres se pueden declarar en varios ensamblados de .NET, es posible tener una situación en la que dos ensamblados definen un tipo con el mismo nombre completo. En ese caso, se prefiere un tipo declarado en el conjunto actual de archivos de origen sobre un tipo declarado en un ensamblado .NET externo. De lo contrario, el nombre es ambiguo y no hay ninguna manera de desambiguar el nombre.
Expresiones AddressOf
Se usa una AddressOf expresión para generar un puntero de método. La expresión consta de la AddressOf palabra clave y una expresión que se debe clasificar como un grupo de métodos o un acceso enlazado en tiempo de ejecución. El grupo de métodos no puede hacer referencia a constructores.
El resultado se clasifica como puntero de método, con la misma expresión de destino asociada y la misma lista de argumentos de tipo (si existe) que el grupo de métodos.
AddressOfExpression
: 'AddressOf' Expression
;
Expresiones de tipo
Una expresión de tipo es una GetType expresión, una TypeOf...Is expresión, una Is expresión o una GetXmlNamespace expresión.
TypeExpression
: GetTypeExpression
| TypeOfIsExpression
| IsExpression
| GetXmlNamespaceExpression
;
Expresiones GetType
Una GetType expresión consta de la palabra clave GetType y el nombre de un tipo.
GetTypeExpression
: 'GetType' OpenParenthesis GetTypeTypeName CloseParenthesis
;
GetTypeTypeName
: TypeName
| QualifiedOpenTypeName
;
QualifiedOpenTypeName
: Identifier TypeArityList? (Period IdentifierOrKeyword TypeArityList?)*
| 'Global' Period IdentifierOrKeyword TypeArityList?
(Period IdentifierOrKeyword TypeArityList?)*
;
TypeArityList
: OpenParenthesis 'Of' CommaList? CloseParenthesis
;
CommaList
: Comma Comma*
;
Una GetType expresión se clasifica como un valor y su valor es la clase de reflexión (System.Type) que representa su GetTypeTypeName. Si GetTypeTypeName es un parámetro de tipo, la expresión devolverá el System.Type objeto que corresponde al argumento de tipo proporcionado para el parámetro de tipo en tiempo de ejecución.
GetTypeTypeName es especial de dos maneras:
Se permite que sea
System.Void, el único lugar en el idioma al que se puede hacer referencia a este nombre de tipo.Puede ser un tipo genérico construido con los argumentos de tipo omitidos. Esto permite que la
GetTypeexpresión devuelva elSystem.Typeobjeto que corresponde al propio tipo genérico.
En el ejemplo siguiente se muestra la GetType expresión :
Module Test
Sub Main()
Dim t As Type() = { GetType(Integer), GetType(System.Int32), _
GetType(String), GetType(Double()) }
Dim i As Integer
For i = 0 To t.Length - 1
Console.WriteLine(t(i).Name)
Next i
End Sub
End Module
La salida resultante es:
Int32
Int32
String
Double[]
TypeOf... Expresiones is
Se usa una TypeOf...Is expresión para comprobar si el tipo en tiempo de ejecución de un valor es compatible con un tipo determinado. El primer operando debe clasificarse como un valor, no puede ser un método lambda reclasificado y debe ser de un tipo de referencia o de un tipo de parámetro de tipo sin restricciones. El segundo operando debe ser un nombre de tipo. El resultado de la expresión se clasifica como un valor y es un Boolean valor. La expresión se True evalúa como si el tipo en tiempo de ejecución del operando tiene una identidad, un valor predeterminado, una referencia, una matriz, un tipo de valor o una conversión de parámetro de tipo al tipo; de lo contrario, False . Se produce un error en tiempo de compilación si no existe ninguna conversión entre el tipo de la expresión y el tipo específico.
TypeOfIsExpression
: 'TypeOf' Expression 'Is' LineTerminator? TypeName
;
Expresiones is
Se usa una Is expresión o IsNot para realizar una comparación de igualdad de referencia.
IsExpression
: Expression 'Is' LineTerminator? Expression
| Expression 'IsNot' LineTerminator? Expression
;
Cada expresión debe clasificarse como un valor y el tipo de cada expresión debe ser un tipo de referencia, un tipo de parámetro de tipo sin restricciones o un tipo de valor que acepta valores NULL. Si el tipo de una expresión es un tipo de parámetro de tipo sin restricciones o un tipo de valor que acepta valores NULL, la otra expresión debe ser el literal Nothing.
El resultado se clasifica como un valor y se escribe como Boolean. Una Is operación se True evalúa como si ambos valores hacen referencia a la misma instancia o ambos valores son Nothing, o False de lo contrario. Una IsNot operación se False evalúa como si ambos valores hacen referencia a la misma instancia o ambos valores son Nothing, o True de lo contrario.
Expresiones GetXmlNamespace
Una GetXmlNamespace expresión consta de la palabra clave GetXmlNamespace y el nombre de un espacio de nombres XML declarado por el archivo de origen o el entorno de compilación.
GetXmlNamespaceExpression
: 'GetXmlNamespace' OpenParenthesis XMLNamespaceName? CloseParenthesis
;
Una GetXmlNamespace expresión se clasifica como un valor y su valor es una instancia de System.Xml.Linq.XNamespace que representa xmlNamespaceName. Si ese tipo no está disponible, se producirá un error en tiempo de compilación.
Por ejemplo:
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
Dim db = GetXmlNamespace(db)
' The following are equivalent
Dim customer1 = _
New System.Xml.Linq.XElement(db + "customer", "Bob")
Dim customer2 = <db:customer>Bob</>
End Sub
End Module
Todo lo que hay entre paréntesis se considera parte del nombre del espacio de nombres, por lo que se aplican reglas XML en torno a elementos como el espacio en blanco. Por ejemplo:
Imports <xmlns:db-ns="http://example.org/database">
Module Test
Sub Main()
' Error, XML name expected
Dim db1 = GetXmlNamespace( db-ns )
' Error, ')' expected
Dim db2 = GetXmlNamespace(db _
)
' OK.
Dim db3 = GetXmlNamespace(db-ns)
End Sub
End Module
También se puede omitir la expresión de espacio de nombres XML, en cuyo caso la expresión devuelve el objeto que representa el espacio de nombres XML predeterminado.
Expresiones de acceso a miembros
Una expresión de acceso de miembro se usa para acceder a un miembro de una entidad.
MemberAccessExpression
: MemberAccessBase? Period IdentifierOrKeyword
( OpenParenthesis 'Of' TypeArgumentList CloseParenthesis )?
;
MemberAccessBase
: Expression
| NonArrayTypeName
| 'Global'
| 'MyClass'
| 'MyBase'
;
Un acceso de miembro del formulario E.I(Of A), donde E es una expresión, un nombre de tipo que no es de matriz, la palabra clave Global, o se omite y I es un identificador con una lista Ade argumentos de tipo opcional , se evalúa y clasifica de la siguiente manera:
Si
Ese omite, la expresión de la instrucción contenedoraWithinmediatamente se sustituye porEy se realiza el acceso de miembro. Si no hay ninguna instrucción contenedoraWith, se produce un error en tiempo de compilación.Si
Ese clasifica como un espacio de nombres oEes la palabra claveGlobal, la búsqueda de miembros se realiza en el contexto del espacio de nombres especificado. SiIes el nombre de un miembro accesible de ese espacio de nombres con el mismo número de parámetros de tipo que se proporcionó en la lista de argumentos de tipo, si existe, el resultado es ese miembro. El resultado se clasifica como un espacio de nombres o un tipo en función del miembro. De lo contrario, se produce un error en tiempo de compilación.Si
Ees un tipo o una expresión clasificada como un tipo, la búsqueda de miembros se realiza en el contexto del tipo especificado. SiIes el nombre de un miembro accesible deE,E.Ise evalúa y clasifica de la siguiente manera:- Si
Ies la palabra claveNewyEno es una enumeración, se produce un error en tiempo de compilación. - Si
Iidentifica un tipo con el mismo número de parámetros de tipo que se proporcionó en la lista de argumentos de tipo, si existe, el resultado es ese tipo. - Si
Iidentifica uno o varios métodos, el resultado es un grupo de métodos con la lista de argumentos de tipo asociado y ninguna expresión de destino asociada. - Si
Iidentifica una o varias propiedades y no se proporcionó ninguna lista de argumentos de tipo, el resultado es un grupo de propiedades sin ninguna expresión de destino asociada. - Si
Iidentifica una variable compartida y no se proporcionó ninguna lista de argumentos de tipo, el resultado es una variable o un valor. Si la variable es de solo lectura y la referencia se produce fuera del constructor compartido del tipo en el que se declara la variable, el resultado es el valor de la variableIcompartida enE. De lo contrario, el resultado es la variableIcompartida enE. - Si
Iidentifica un evento compartido y no se proporcionó ninguna lista de argumentos de tipo, el resultado es un acceso a eventos sin ninguna expresión de destino asociada. - Si
Iidentifica una constante y no se proporcionó ninguna lista de argumentos de tipo, el resultado es el valor de esa constante. - Si
Iidentifica un miembro de enumeración y no se proporcionó ninguna lista de argumentos de tipo, el resultado es el valor de ese miembro de enumeración. - En caso contrario,
E.Ies una referencia de miembro no válida y se produce un error de compilación.
- Si
Si
Ese clasifica como una variable o un valor, el tipo de que esT, la búsqueda de miembros se realiza en el contexto deT. SiIes el nombre de un miembro accesible deT,E.Ise evalúa y clasifica de la siguiente manera:- Si
Ies la palabra claveNew,EesMe,MyBaseoMyClass, y no se proporcionó ningún argumento de tipo, el resultado es un grupo de métodos que representa los constructores de instancia del tipo deEcon una expresión de destino asociada de y ninguna lista deEargumentos de tipo. De lo contrario, se produce un error en tiempo de compilación. - Si
Iidentifica uno o varios métodos, incluidos los métodos de extensión siTnoObjectes , el resultado es un grupo de métodos con la lista de argumentos de tipo asociado y una expresión de destino asociada deE. - Si
Iidentifica una o varias propiedades y no se proporcionó ningún argumento de tipo, el resultado es un grupo de propiedades con una expresión de destino asociada deE. - Si
Iidentifica una variable compartida o una variable de instancia y no se proporcionó ningún argumento de tipo, el resultado es una variable o un valor. Si la variable es de solo lectura y la referencia se produce fuera de un constructor de la clase en la que la variable se declara adecuada para el tipo de variable (compartida o instancia), el resultado es el valor de la variableIen el objeto al que haceEreferencia . SiTes un tipo de referencia, el resultado es la variableIdel objeto al que haceEreferencia . De lo contrario, siTes un tipo de valor y la expresiónEse clasifica como una variable, el resultado es una variable; de lo contrario, el resultado es un valor. - Si
Iidentifica un evento y no se proporcionaron argumentos de tipo, el resultado es un acceso de evento con una expresión de destino asociada deE. - Si
Iidentifica una constante y no se proporcionaron argumentos de tipo, el resultado es el valor de esa constante. - Si
Iidentifica un miembro de enumeración y no se proporcionó ningún argumento de tipo, el resultado es el valor de ese miembro de enumeración. - Si
TesObject, el resultado es una búsqueda de miembro enlazada en tiempo de ejecución clasificada como acceso enlazado en tiempo de ejecución con la lista de argumentos de tipo asociado y una expresión de destino asociada deE.
- Si
En caso contrario,
E.Ies una referencia de miembro no válida y se produce un error de compilación.
Un acceso de miembro del formulario MyClass.I(Of A) es equivalente a Me.I(Of A), pero todos los miembros a los que se accede en él se tratan como si los miembros no fueran invalidables. Por lo tanto, el miembro al que se accede no se verá afectado por el tipo en tiempo de ejecución del valor en el que se accede al miembro.
Un acceso de miembro del formulario MyBase.I(Of A) es equivalente a CType(Me, T).I(Of A) donde T es el tipo base directo del tipo que contiene la expresión de acceso de miembro. Todas las invocaciones de método en ella se tratan como si el método que se invoca no es invalidable. Esta forma de acceso de miembro también se denomina acceso base.
En el ejemplo siguiente se muestra cómo Mey MyClassMyBase se relacionan:
Class Base
Public Overridable Sub F()
Console.WriteLine("Base.F")
End Sub
End Class
Class Derived
Inherits Base
Public Overrides Sub F()
Console.WriteLine("Derived.F")
End Sub
Public Sub G()
MyClass.F()
End Sub
End Class
Class MoreDerived
Inherits Derived
Public Overrides Sub F()
Console.WriteLine("MoreDerived.F")
End Sub
Public Sub H()
MyBase.F()
End Sub
End Class
Module Test
Sub Main()
Dim x As MoreDerived = new MoreDerived()
x.F()
x.G()
x.H()
End Sub
End Module
Este código imprime:
MoreDerived.F
Derived.F
Derived.F
Cuando una expresión de acceso de miembro comienza con la palabra clave Global, la palabra clave representa el espacio de nombres sin nombre más externo, que es útil en situaciones en las que una declaración sombrea un espacio de nombres envolvente. La Global palabra clave permite "escapar" al espacio de nombres más externo en esa situación. Por ejemplo:
Class System
End Class
Module Test
Sub Main()
' Error: Class System does not contain Console
System.Console.WriteLine("Hello, world!")
' Legal, binds to System in outermost namespace
Global.System.Console.WriteLine("Hello, world!")
End Sub
End Module
En el ejemplo anterior, la primera llamada al método no es válida porque el identificador System se enlaza a la clase System, no al espacio de nombres System. La única manera de acceder al System espacio de nombres es usar Global para escapar al espacio de nombres más externo.
Si se comparte el miembro al que se accede, cualquier expresión del lado izquierdo del período es superflua y no se evalúa a menos que el acceso al miembro se realice en tiempo de ejecución. Por ejemplo, considere el código siguiente:
Class C
Public Shared F As Integer = 10
End Class
Module Test
Public Function ReturnC() As C
Console.WriteLine("Returning a new instance of C.")
Return New C()
End Function
Public Sub Main()
Console.WriteLine("The value of F is: " & ReturnC().F)
End Sub
End Module
Se imprime The value of F is: 10 porque no es necesario llamar a la función ReturnC para proporcionar una instancia de C para acceder al miembro Fcompartido .
Nombres de miembro y tipo idénticos
No es raro asignar nombres a los miembros con el mismo nombre que su tipo. Sin embargo, en esa situación, puede producirse una ocultación de nombres inconveniente:
Enum Color
Red
Green
Yellow
End Enum
Class Test
ReadOnly Property Color() As Color
Get
Return Color.Red
End Get
End Property
Shared Function DefaultColor() As Color
Return Color.Green ' Binds to the instance property!
End Function
End Class
En el ejemplo anterior, el nombre Color simple de enlaza DefaultColor a la propiedad de instancia en lugar del tipo . Dado que no se puede hacer referencia a un miembro de instancia en un miembro compartido, normalmente se trata de un error.
Sin embargo, una regla especial permite el acceso al tipo en este caso. Si la expresión base de una expresión de acceso de miembro es un nombre simple y se enlaza a una constante, campo, propiedad, variable local o parámetro cuyo tipo tiene el mismo nombre, la expresión base puede hacer referencia al miembro o al tipo. Esto nunca puede dar lugar a ambigüedad porque los miembros a los que se puede tener acceso fuera de cualquiera de ellos son los mismos.
Instancias predeterminadas
En algunas situaciones, las clases derivadas de una clase base común normalmente o siempre tienen una sola instancia. Por ejemplo, la mayoría de las ventanas mostradas en una interfaz de usuario solo tienen una instancia que se muestra en la pantalla en cualquier momento. Para simplificar el trabajo con estos tipos de clases, Visual Basic puede generar automáticamente instancias predeterminadas de las clases que proporcionan una única instancia a la que se hace referencia fácilmente para cada clase.
Las instancias predeterminadas siempre se crean para una familia de tipos en lugar de para un tipo determinado. Por lo tanto, en lugar de crear una instancia predeterminada para una clase Form1 que deriva de Form, se crean instancias predeterminadas para todas las clases derivadas de Form. Esto significa que cada clase individual que deriva de la clase base no tiene que marcarse especialmente para tener una instancia predeterminada.
La instancia predeterminada de una clase se representa mediante una propiedad generada por el compilador que devuelve la instancia predeterminada de esa clase. La propiedad generada como miembro de una clase denominada clase de grupo que administra la asignación y destrucción de instancias predeterminadas para todas las clases derivadas de la clase base determinada. Por ejemplo, todas las propiedades de instancia predeterminadas de las clases derivadas Form de se pueden recopilar en la MyForms clase . Si la expresión My.Formsdevuelve una instancia de la clase de grupo , el código siguiente tiene acceso a las instancias predeterminadas de las clases derivadas Form1 y Form2:
Class Form1
Inherits Form
Public x As Integer
End Class
Class Form2
Inherits Form
Public y As Integer
End Class
Module Main
Sub Main()
My.Forms.Form1.x = 10
Console.WriteLine(My.Forms.Form2.y)
End Sub
End Module
Las instancias predeterminadas no se crearán hasta la primera referencia a ellas; Capturar la propiedad que representa la instancia predeterminada hace que se cree la instancia predeterminada si aún no se ha creado o se ha establecido en Nothing. Para permitir pruebas para la existencia de una instancia predeterminada, cuando una instancia predeterminada es el destino de un Is operador o IsNot , no se creará la instancia predeterminada. Por lo tanto, es posible probar si una instancia predeterminada es Nothing o alguna otra referencia sin hacer que se cree la instancia predeterminada.
Las instancias predeterminadas están diseñadas para facilitar la referencia a la instancia predeterminada desde fuera de la clase que tiene la instancia predeterminada. El uso de una instancia predeterminada desde dentro de una clase que define podría provocar confusión en cuanto a la instancia a la que se hace referencia, es decir, la instancia predeterminada o la instancia actual. Por ejemplo, el código siguiente modifica solo el valor x de la instancia predeterminada, aunque se llame desde otra instancia. Por lo tanto, el código imprimiría el valor 5 en lugar de 10:
Class Form1
Inherits Form
Public x As Integer = 5
Public Sub ChangeX()
Form1.x = 10
End Sub
End Class
Module Main
Sub Main()
Dim f As Form1 = New Form1()
f.ChangeX()
Console.WriteLine(f.x)
End Sub
End Module
Para evitar este tipo de confusión, no es válido hacer referencia a una instancia predeterminada desde un método de instancia del tipo de instancia predeterminado.
Instancias predeterminadas y nombres de tipo
También se puede acceder a una instancia predeterminada directamente a través del nombre de su tipo. En este caso, en cualquier contexto de expresión en el que no se permita el nombre de tipo, Edonde E representa el nombre completo de la clase con una instancia predeterminada, se cambia a E', donde E' representa una expresión que captura la propiedad de instancia predeterminada. Por ejemplo, si las instancias predeterminadas de las clases derivadas de Form permiten acceder a la instancia predeterminada a través del nombre de tipo, el código siguiente es equivalente al código del ejemplo anterior:
Module Main
Sub Main()
Form1.x = 10
Console.WriteLine(Form2.y)
End Sub
End Module
Esto también significa que una instancia predeterminada a la que se puede acceder a través del nombre de su tipo también se puede asignar a través del nombre de tipo. Por ejemplo, el código siguiente establece la instancia predeterminada de Form1 en Nothing:
Module Main
Sub Main()
Form1 = Nothing
End Sub
End Module
Tenga en cuenta que el significado de E.I se E representa una clase y I representa un miembro compartido no cambia. Esta expresión sigue teniendo acceso al miembro compartido directamente fuera de la instancia de clase y no hace referencia a la instancia predeterminada.
Clases de grupo
El Microsoft.VisualBasic.MyGroupCollectionAttribute atributo indica la clase de grupo para una familia de instancias predeterminadas. El atributo tiene cuatro parámetros:
El parámetro
TypeToCollectespecifica la clase base para el grupo. Todas las clases que se pueden crear instancias sin parámetros de tipo abiertos que derivan de un tipo con este nombre (independientemente de los parámetros de tipo) tendrán automáticamente una instancia predeterminada.El parámetro
CreateInstanceMethodNameespecifica el método al que llamar en la clase de grupo para crear una nueva instancia en una propiedad de instancia predeterminada.El parámetro
DisposeInstanceMethodNameespecifica el método al que llamar en la clase de grupo para desechar una propiedad de instancia predeterminada si se asigna el valorNothinga la propiedad de instancia predeterminada .El parámetro
DefaultInstanceAliasespecifica la expresiónE'que se va a sustituir por el nombre de clase si las instancias predeterminadas son accesibles directamente a través de su nombre de tipo. Si este parámetro esNothingo una cadena vacía, las instancias predeterminadas de este tipo de grupo no son accesibles directamente a través del nombre de su tipo. (Nota. En todas las implementaciones actuales del lenguaje Visual Basic, se omite elDefaultInstanceAliasparámetro, excepto en el código proporcionado por el compilador).
Se pueden recopilar varios tipos en el mismo grupo separando los nombres de los tipos y métodos en los tres primeros parámetros mediante comas. Debe haber el mismo número de elementos en cada parámetro y los elementos de lista coinciden en orden. Por ejemplo, la siguiente declaración de atributo recopila tipos que derivan de C1o C3C2 en un único grupo:
<Microsoft.VisualBasic.MyGroupCollection("C1, C2, C3", _
"CreateC1, CreateC2, CreateC3", _
"DisposeC1, DisposeC2, DisposeC3", "My.Cs")>
Public NotInheritable Class MyCs
...
End Class
La firma del método create debe tener el formato Shared Function <Name>(Of T As {New, <Type>})(Instance Of T) As T. El método dispose debe tener el formato Shared Sub <Name>(Of T As <Type>)(ByRef Instance Of T). Por lo tanto, la clase de grupo para el ejemplo de la sección anterior podría declararse de la siguiente manera:
<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", _
"Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
Private Shared Function Create(Of T As {New, Form}) _
(Instance As T) As T
If Instance Is Nothing Then
Return New T()
Else
Return Instance
End If
End Function
Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
Instance.Close()
Instance = Nothing
End Sub
End Class
Si un archivo de origen declaró una clase Form1derivada, la clase de grupo generada sería equivalente a:
<Microsoft.VisualBasic.MyGroupCollection("Form", "Create", _
"Dispose", "My.Forms")> _
Public NotInheritable Class MyForms
Private Shared Function Create(Of T As {New, Form}) _
(Instance As T) As T
If Instance Is Nothing Then
Return New T()
Else
Return Instance
End If
End Function
Private Shared Sub Dispose(Of T As Form)(ByRef Instance As T)
Instance.Close()
Instance = Nothing
End Sub
Private m_Form1 As Form1
Public Property Form1() As Form1
Get
Return Create(m_Form1)
End Get
Set (Value As Form1)
If Value IsNot Nothing AndAlso Value IsNot m_Form1 Then
Throw New ArgumentException( _
"Property can only be set to Nothing.")
End If
Dispose(m_Form1)
End Set
End Property
End Class
Colección de métodos de extensión
Los métodos de extensión para la expresión E.I de acceso de miembro se recopilan recopilando todos los métodos de extensión con el nombre I que están disponibles en el contexto actual:
- En primer lugar, se comprueba cada tipo anidado que contiene la expresión, comenzando desde el más interno y pasando al exterior más externo.
- A continuación, se comprueba cada espacio de nombres anidado, empezando por el más interno y pasando al espacio de nombres más externo.
- A continuación, se comprueban las importaciones en el archivo de origen.
- A continuación, se comprueban las importaciones definidas por el entorno de compilación.
Un método de extensión solo se recopila si hay una conversión nativa de ampliación del tipo de expresión de destino al tipo del primer parámetro del método de extensión. Y, a diferencia del enlace regular de expresiones de nombre simple, la búsqueda recopila todos los métodos de extensión; la colección no se detiene cuando se encuentra un método de extensión. Por ejemplo:
Imports System.Runtime.CompilerServices
Class C1
End Class
Namespace N1
Module N1C1Extensions
<Extension> _
Sub M1(c As C1, x As Integer)
End Sub
End Module
End Namespace
Namespace N1.N2
Module N2C1Extensions
<Extension> _
Sub M1(c As C1, y As Double)
End Sub
End Module
End Namespace
Namespace N1.N2.N3
Module Test
Sub Main()
Dim x As New C1()
' Calls N1C1Extensions.M1
x.M1(10)
End Sub
End Module
End Namespace
En este ejemplo, aunque N2C1Extensions.M1 se encuentre antes N1C1Extensions.M1de , ambos se consideran métodos de extensión. Una vez recopilados todos los métodos de extensión, se consultan. Currying toma el destino de la llamada al método de extensión y lo aplica a la llamada al método de extensión, lo que da lugar a una nueva firma de método con el primer parámetro quitado (porque se ha especificado). Por ejemplo:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M(x As Integer, y As Integer)
End Sub
End Module
Module Ext2
<Extension> _
Sub M(x As Integer, y As Double)
End Sub
End Module
Module Main
Sub Test()
Dim v As Integer = 10
' The curried method signatures considered are:
' Ext1.M(y As Integer)
' Ext2.M(y As Double)
v.M(10)
End Sub
End Module
En el ejemplo anterior, el resultado curried de aplicar v a Ext1.M es la firma Sub M(y As Integer)del método .
Además de quitar el primer parámetro del método de extensión, currying también quita los parámetros de tipo de método que forman parte del tipo del primer parámetro. Cuando se cambia un método de extensión con el parámetro de tipo de método, la inferencia de tipos se aplica al primer parámetro y el resultado se fija para cualquier parámetro de tipo que se infiere. Si se produce un error en la inferencia de tipos, se omite el método . Por ejemplo:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M(Of T, U)(x As T, y As U)
End Sub
End Module
Module Ext2
<Extension> _
Sub M(Of T)(x As T, y As T)
End Sub
End Module
Module Main
Sub Test()
Dim v As Integer = 10
' The curried method signatures considered are:
' Ext1.M(Of U)(y As U)
' Ext2.M(y As Integer)
v.M(10)
End Sub
End Module
En el ejemplo anterior, el resultado curried de aplicar v a Ext1.M es la firma Sub M(Of U)(y As U)del método , ya que el parámetro T de tipo se deduce como resultado del curriing y ahora es fijo. Dado que el parámetro U de tipo no se infirió como parte del currying, sigue siendo un parámetro abierto. Del mismo modo, dado que el parámetro T de tipo se deduce como resultado de aplicar v a Ext2.M, el tipo de parámetro y se fija como Integer. No se deducirá que sea de ningún otro tipo. Al aplicar la firma, también se aplican todas las restricciones, excepto New las restricciones. Si no se cumplen las restricciones, o dependen de un tipo que no se infiere como parte del currying, se omite el método de extensión. Por ejemplo:
Imports System.Runtime.CompilerServices
Module Ext1
<Extension> _
Sub M1(Of T As Structure)(x As T, y As Integer)
End Sub
<Extension> _
Sub M2(Of T As U, U)(x As T, y As U)
End Sub
End Module
Module Main
Sub Test()
Dim s As String = "abc"
' Error: String does not satisfy the Structure constraint
s.M1(10)
' Error: T depends on U, which cannot be inferred
s.M2(10)
End Sub
End Module
Nota. Una de las principales razones para hacer el currying de métodos de extensión es que permite a las expresiones de consulta deducir el tipo de la iteración antes de evaluar los argumentos en un método de patrón de consulta. Dado que la mayoría de los métodos de patrón de consulta toman expresiones lambda, que requieren la inferencia de tipos, esto simplifica considerablemente el proceso de evaluación de una expresión de consulta.
A diferencia de la herencia de interfaz normal, los métodos de extensión que extienden dos interfaces que no se relacionan entre sí están disponibles, siempre y cuando no tengan la misma firma curriada:
Imports System.Runtime.CompilerServices
Interface I1
End Interface
Interface I2
End Interface
Class C1
Implements I1, I2
End Class
Module I1Ext
<Extension> _
Sub M1(i As I1, x As Integer)
End Sub
<Extension> _
Sub M2(i As I1, x As Integer)
End Sub
End Module
Module I2Ext
<Extension> _
Sub M1(i As I2, x As Integer)
End Sub
<Extension> _
Sub M2(I As I2, x As Double)
End Sub
End Module
Module Main
Sub Test()
Dim c As New C1()
' Error: M is ambiguous between I1Ext.M1 and I2Ext.M1.
c.M1(10)
' Calls I1Ext.M2
c.M2(10)
End Sub
End Module
Por último, es importante recordar que los métodos de extensión no se tienen en cuenta al realizar el enlace en tiempo de ejecución:
Module Test
Sub Main()
Dim o As Object = ...
' Ignores extension methods
o.M1()
End Sub
End Module
Expresiones de acceso a miembros de diccionario
Se usa una expresión de acceso de miembro de diccionario para buscar un miembro de una colección. Un acceso de miembro de diccionario tiene la forma de E!I, donde E es una expresión que se clasifica como un valor y I es un identificador.
DictionaryAccessExpression
: Expression? '!' IdentifierOrKeyword
;
El tipo de la expresión debe tener una propiedad predeterminada indizada por un único String parámetro. La expresión E!I de acceso de miembro del diccionario se transforma en la expresión E.D("I"), donde D es la propiedad predeterminada de E. Por ejemplo:
Class Keys
Public ReadOnly Default Property Item(s As String) As Integer
Get
Return 10
End Get
End Property
End Class
Module Test
Sub Main()
Dim x As Keys = new Keys()
Dim y As Integer
' The two statements are equivalent.
y = x!abc
y = x("abc")
End Sub
End Module
Si se especifica un signo de exclamación sin expresión, se asume la expresión de la instrucción contenedora With inmediatamente. Si no hay ninguna instrucción contenedora With , se produce un error en tiempo de compilación.
Expresiones de invocación
Una expresión de invocación consta de un destino de invocación y una lista de argumentos opcional.
InvocationExpression
: Expression ( OpenParenthesis ArgumentList? CloseParenthesis )?
;
ArgumentList
: PositionalArgumentList
| PositionalArgumentList Comma NamedArgumentList
| NamedArgumentList
;
PositionalArgumentList
: Expression? ( Comma Expression? )*
;
NamedArgumentList
: IdentifierOrKeyword ColonEquals Expression
( Comma IdentifierOrKeyword ColonEquals Expression )*
;
La expresión de destino debe clasificarse como un grupo de métodos o un valor cuyo tipo sea un tipo delegado. Si la expresión de destino es un valor cuyo tipo es un tipo delegado, el destino de la expresión de invocación se convierte en el grupo de métodos para el Invoke miembro del tipo delegado y la expresión de destino se convierte en la expresión de destino asociada del grupo de métodos.
Una lista de argumentos tiene dos secciones: argumentos posicionales y argumentos con nombre.
Los argumentos posicionales son expresiones y deben preceder a los argumentos con nombre.
Los argumentos con nombre comienzan con un identificador que puede coincidir con palabras clave, seguidos de := y una expresión.
Si el grupo de métodos solo contiene un método accesible, incluidos los métodos de instancia y extensión, y ese método no toma argumentos y es una función, el grupo de métodos se interpreta como una expresión de invocación con una lista de argumentos vacío y el resultado se usa como destino de una expresión de invocación con las listas de argumentos proporcionados. Por ejemplo:
Class C1
Function M1() As Integer()
Return New Integer() { 1, 2, 3 }
End Sub
End Class
Module Test
Sub Main()
Dim c As New C1()
' Prints 3
Console.WriteLine(c.M1(2))
End Sub
End Module
De lo contrario, la resolución de sobrecarga se aplica a los métodos para elegir el método más aplicable para las listas de argumentos especificadas. Si el método más aplicable es una función, el resultado de la expresión de invocación se clasifica como un valor escrito como el tipo de valor devuelto de la función. Si el método más aplicable es una subrutina, el resultado se clasifica como void. Si el método más aplicable es un método parcial que no tiene ningún cuerpo, se omite la expresión de invocación y el resultado se clasifica como void.
Para una expresión de invocación enlazada inicialmente, los argumentos se evalúan en el orden en que los parámetros correspondientes se declaran en el método de destino. Para una expresión de acceso de miembro enlazada en tiempo de ejecución, se evalúan en el orden en que aparecen en la expresión de acceso de miembro: vea Section Late-Bound Expressions.
Resolución de métodos sobrecargados:
Para la resolución de sobrecargas, la especificidad de los miembros o tipos dados una lista de argumentos, genericidad, aplicabilidad a la lista de argumentos, pasar argumentos y seleccionar argumentos para parámetros opcionales, métodos condicionales e inferencia de argumentos de tipo: vea Resolución de sobrecarga de sección.
Expresiones de índice
Una expresión de índice da como resultado un elemento de matriz o vuelve a clasificar un grupo de propiedades en un acceso a propiedades. Una expresión de índice consta de, en orden, una expresión, un paréntesis de apertura, una lista de argumentos de índice y un paréntesis de cierre.
IndexExpression
: Expression OpenParenthesis ArgumentList? CloseParenthesis
;
El destino de la expresión de índice debe clasificarse como un grupo de propiedades o un valor. Una expresión de índice se procesa de la siguiente manera:
Si la expresión de destino se clasifica como un valor y si su tipo no es un tipo de matriz,
Object, oSystem.Array, el tipo debe tener una propiedad predeterminada. El índice se realiza en un grupo de propiedades que representa todas las propiedades predeterminadas del tipo. Aunque no es válido declarar una propiedad predeterminada sin parámetros en Visual Basic, otros lenguajes pueden permitir declarar dicha propiedad. Por lo tanto, se permite la indexación de una propiedad sin argumentos.Si la expresión da como resultado un valor de un tipo de matriz, el número de argumentos de la lista de argumentos debe ser el mismo que el rango del tipo de matriz y puede no incluir argumentos con nombre. Si alguno de los índices no es válido en tiempo de ejecución, se produce una
System.IndexOutOfRangeExceptionexcepción. Cada expresión debe convertirse implícitamente en el tipoInteger. El resultado de la expresión de índice es la variable en el índice especificado y se clasifica como una variable.Si la expresión se clasifica como un grupo de propiedades, la resolución de sobrecarga se usa para determinar si una de las propiedades es aplicable a la lista de argumentos de índice. Si el grupo de propiedades solo contiene una propiedad que tiene un
Getdescriptor de acceso y si ese descriptor de acceso no toma ningún argumento, el grupo de propiedades se interpreta como una expresión de índice con una lista de argumentos vacía. El resultado se usa como destino de la expresión de índice actual. Si no se aplica ninguna propiedad, se produce un error en tiempo de compilación. De lo contrario, la expresión da como resultado un acceso de propiedad con la expresión de destino asociada (si existe) del grupo de propiedades.Si la expresión se clasifica como un grupo de propiedades enlazados en tiempo de ejecución o como un valor cuyo tipo es
ObjectoSystem.Array, el procesamiento de la expresión de índice se aplaza hasta el tiempo de ejecución y la indexación está enlazada en tiempo de ejecución. La expresión da como resultado un acceso de propiedad enlazado en tiempo de ejecución escrito comoObject. La expresión de destino asociada es la expresión de destino, si es un valor o la expresión de destino asociada del grupo de propiedades. En tiempo de ejecución, la expresión se procesa de la siguiente manera:Si la expresión se clasifica como un grupo de propiedades enlazados en tiempo de ejecución, la expresión puede dar lugar a un grupo de métodos, a un grupo de propiedades o a un valor (si el miembro es una instancia o una variable compartida). Si el resultado es un grupo de métodos o un grupo de propiedades, la resolución de sobrecarga se aplica al grupo para determinar el método correcto para la lista de argumentos. Si se produce un error en la resolución de sobrecarga, se produce una
System.Reflection.AmbiguousMatchExceptionexcepción. A continuación, el resultado se procesa como acceso de propiedad o como invocación y se devuelve el resultado. Si la invocación es de una subrutina, el resultado esNothing.Si el tipo en tiempo de ejecución de la expresión de destino es un tipo de matriz o
System.Array, el resultado de la expresión de índice es el valor de la variable en el índice especificado.De lo contrario, el tipo en tiempo de ejecución de la expresión debe tener una propiedad predeterminada y el índice se realiza en el grupo de propiedades que representa todas las propiedades predeterminadas del tipo. Si el tipo no tiene ninguna propiedad predeterminada, se produce una
System.MissingMemberExceptionexcepción.
Expresiones nuevas
El operador New se utiliza para crear nuevas instancias de tipos. Hay cuatro formas de New expresiones:
Las expresiones de creación de objetos se usan para crear nuevas instancias de tipos de clase y tipos de valor.
Las expresiones de creación de matrices se usan para crear nuevas instancias de tipos de matriz.
Las expresiones de creación de delegados (que no tienen una sintaxis distinta de las expresiones de creación de objetos) se usan para crear nuevas instancias de tipos delegados.
Las expresiones anónimas de creación de objetos se usan para crear nuevas instancias de tipos de clase anónimos.
NewExpression
: ObjectCreationExpression
| ArrayExpression
| AnonymousObjectCreationExpression
;
Una New expresión se clasifica como un valor y el resultado es la nueva instancia del tipo .
expresiones de Object-Creation
Una expresión de creación de objetos se usa para crear una nueva instancia de un tipo de clase o un tipo de estructura.
ObjectCreationExpression
: 'New' NonArrayTypeName ( OpenParenthesis ArgumentList? CloseParenthesis )?
ObjectCreationExpressionInitializer?
;
ObjectCreationExpressionInitializer
: ObjectMemberInitializer
| ObjectCollectionInitializer
;
ObjectMemberInitializer
: 'With' OpenCurlyBrace FieldInitializerList CloseCurlyBrace
;
FieldInitializerList
: FieldInitializer ( Comma FieldInitializer )*
;
FieldInitializer
: 'Key'? ('.' IdentifierOrKeyword Equals )? Expression
;
ObjectCollectionInitializer
: 'From' CollectionInitializer
;
CollectionInitializer
: OpenCurlyBrace CollectionElementList? CloseCurlyBrace
;
CollectionElementList
: CollectionElement ( Comma CollectionElement )*
;
CollectionElement
: Expression
| CollectionInitializer
;
El tipo de una expresión de creación de objetos debe ser un tipo de clase, un tipo de estructura o un parámetro de tipo con una New restricción y no puede ser una MustInherit clase. Dada una expresión de creación de objetos del formulario New T(A), donde T es un tipo de clase o un tipo de estructura y A es una lista de argumentos opcional, la resolución de sobrecarga determina el constructor correcto de T al que llamar. Un parámetro de tipo con una New restricción se considera que tiene un único constructor sin parámetros. Si no se puede llamar a ningún constructor, se produce un error en tiempo de compilación; De lo contrario, la expresión da como resultado la creación de una nueva instancia de T mediante el constructor elegido. Si no hay ningún argumento, se pueden omitir los paréntesis.
Donde se asigna una instancia depende de si la instancia es un tipo de clase o un tipo de valor.
New Las instancias de tipos de clase se crean en el montón del sistema, mientras que las nuevas instancias de tipos de valor se crean directamente en la pila.
Una expresión de creación de objetos puede especificar opcionalmente una lista de inicializadores de miembros después de los argumentos del constructor. Estos inicializadores de miembro tienen como prefijo la palabra clave Withy la lista de inicializadores se interpreta como si estuviera en el contexto de una With instrucción . Por ejemplo, dada la clase :
Class Customer
Dim Name As String
Dim Address As String
End Class
El código:
Module Test
Sub Main()
Dim x As New Customer() With { .Name = "Bob Smith", _
.Address = "123 Main St." }
End Sub
End Module
Es aproximadamente equivalente a:
Module Test
Sub Main()
Dim x, _t1 As Customer
_t1 = New Customer()
With _t1
.Name = "Bob Smith"
.Address = "123 Main St."
End With
x = _t1
End Sub
End Module
Cada inicializador debe especificar un nombre para asignar y el nombre debe ser una variable o propiedad que noReadOnly sea de instancia del tipo que se va a construir; el acceso de miembro no se enlazará en tiempo de ejecución si el tipo que se va a construir es Object. Es posible que los inicializadores no usen la Key palabra clave . Cada miembro de un tipo solo se puede inicializar una vez. Sin embargo, las expresiones del inicializador pueden hacer referencia entre sí. Por ejemplo:
Module Test
Sub Main()
Dim x As New Customer() With { .Name = "Bob Smith", _
.Address = .Name & " St." }
End Sub
End Module
Los inicializadores se asignan de izquierda a derecha, por lo que si un inicializador hace referencia a un miembro que aún no se ha inicializado, verá cualquier valor que la variable de instancia después de ejecutar el constructor:
Module Test
Sub Main()
' The value of Address will be " St." since Name has not been
' assigned yet.
Dim x As New Customer() With { .Address = .Name & " St." }
End Sub
End Module
Los inicializadores se pueden anidar:
Class Customer
Dim Name As String
Dim Address As Address
Dim Age As Integer
End Class
Class Address
Dim Street As String
Dim City As String
Dim State As String
Dim ZIP As String
End Class
Module Test
Sub Main()
Dim c As New Customer() With { _
.Name = "John Smith", _
.Address = New Address() With { _
.Street = "23 Main St.", _
.City = "Peoria", _
.State = "IL", _
.ZIP = "13934" }, _
.Age = 34 }
End Sub
End Module
Si el tipo que se crea es un tipo de colección y tiene un método de instancia denominado Add (incluidos los métodos de extensión y los métodos compartidos), la expresión de creación de objetos puede especificar un inicializador de colección prefijo por la palabra clave From. Una expresión de creación de objetos no puede especificar un inicializador de miembro y un inicializador de colección. Cada elemento del inicializador de colección se pasa como argumento a una invocación de la Add función. Por ejemplo:
Dim list = New List(Of Integer)() From { 1, 2, 3, 4 }
es equivalente a:
Dim list = New List(Of Integer)()
list.Add(1)
list.Add(2)
list.Add(3)
Si un elemento es un inicializador de colección, cada elemento del inicializador de la sub-colección se pasará como argumento individual a la Add función. Por ejemplo, lo siguiente:
Dim dict = Dictionary(Of Integer, String) From { { 1, "One" },{ 2, "Two" } }
es equivalente a:
Dim dict = New Dictionary(Of Integer, String)
dict.Add(1, "One")
dict.Add(2, "Two")
Esta expansión siempre se realiza y solo se hace en un nivel profundo; después de eso, los sub inicializadores se consideran literales de matriz. Por ejemplo:
' Error: List(Of T) does not have an Add method that takes two parameters.
Dim list = New List(Of Integer())() From { { 1, 2 }, { 3, 4 } }
' OK, this initializes the dictionary with (Integer, Integer()) pairs.
Dim dict = New Dictionary(Of Integer, Integer())() From _
{ { 1, { 2, 3 } }, { 3, { 4, 5 } } }
Expresiones de matriz
Una expresión de matriz se usa para crear una nueva instancia de un tipo de matriz. Hay dos tipos de expresiones de matriz: expresiones de creación de matrices y literales de matriz.
Expresiones de creación de matrices
Si se proporciona un modificador de inicialización de tamaño de matriz, el tipo de matriz resultante se deriva eliminando cada uno de los argumentos individuales de la lista de argumentos de inicialización de tamaño de matriz. El valor de cada argumento determina el límite superior de la dimensión correspondiente en la instancia de matriz recién asignada. Si la expresión tiene un inicializador de colección no vacío, cada argumento de la lista de argumentos debe ser una constante y las longitudes de rango y dimensión especificadas por la lista de expresiones deben coincidir con las del inicializador de colección.
Dim a() As Integer = New Integer(2) {}
Dim b() As Integer = New Integer(2) { 1, 2, 3 }
Dim c(,) As Integer = New Integer(1, 2) { { 1, 2, 3 } , { 4, 5, 6 } }
' Error, length/initializer mismatch.
Dim d() As Integer = New Integer(2) { 0, 1, 2, 3 }
Si no se proporciona un modificador de inicialización de tamaño de matriz, el nombre del tipo debe ser un tipo de matriz y el inicializador de colección debe estar vacío o tener el mismo número de niveles de anidamiento que el rango del tipo de matriz especificado. Todos los elementos del nivel de anidamiento más interno deben convertirse implícitamente en el tipo de elemento de la matriz y deben clasificarse como un valor. El número de elementos de cada inicializador de colección anidada siempre debe ser coherente con el tamaño de las otras colecciones en el mismo nivel. Las longitudes de dimensión individuales se deducen del número de elementos de cada uno de los niveles de anidamiento correspondientes del inicializador de colección. Si el inicializador de colección está vacío, la longitud de cada dimensión es cero.
Dim e() As Integer = New Integer() { 1, 2, 3 }
Dim f(,) As Integer = New Integer(,) { { 1, 2, 3 } , { 4, 5, 6 } }
' Error: Inconsistent numbers of elements!
Dim g(,) As Integer = New Integer(,) { { 1, 2 }, { 4, 5, 6 } }
' Error: Inconsistent levels of nesting!
Dim h(,) As Integer = New Integer(,) { 1, 2, { 3, 4 } }
El nivel de anidamiento más externo de un inicializador de colección corresponde a la dimensión situada más a la izquierda de una matriz y el nivel de anidamiento más interno corresponde a la dimensión situada más a la derecha. El ejemplo:
Dim array As Integer(,) = _
{ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 }, { 8, 9 } }
Equivale a lo siguiente:
Dim array(4, 1) As Integer
array(0, 0) = 0: array(0, 1) = 1
array(1, 0) = 2: array(1, 1) = 3
array(2, 0) = 4: array(2, 1) = 5
array(3, 0) = 6: array(3, 1) = 7
array(4, 0) = 8: array(4, 1) = 9
Si el inicializador de colección está vacío (es decir, uno que contiene llaves pero sin lista de inicializadores) y se conocen los límites de las dimensiones de la matriz que se está inicializando, el inicializador de colección vacío representa una instancia de matriz del tamaño especificado donde todos los elementos se han inicializado en el valor predeterminado del tipo de elemento. Si no se conocen los límites de las dimensiones de la matriz que se está inicializando, el inicializador de colección vacío representa una instancia de matriz en la que todas las dimensiones tienen el tamaño cero.
La clasificación y la longitud de cada dimensión de una instancia de matriz son constantes durante toda la duración de la instancia. En otras palabras, no es posible cambiar el rango de una instancia de matriz existente ni es posible cambiar el tamaño de sus dimensiones.
Literales de matriz
Un literal de matriz denota una matriz cuyo tipo de elemento, clasificación y límites se deducen de una combinación del contexto de expresión y un inicializador de colección. Esto se explica en Reclasificación de expresiones de sección.
ArrayExpression
: ArrayCreationExpression
| ArrayLiteralExpression
;
ArrayCreationExpression
: 'New' NonArrayTypeName ArrayNameModifier CollectionInitializer
;
ArrayLiteralExpression
: CollectionInitializer
;
Por ejemplo:
' array of integers
Dim a = {1, 2, 3}
' array of shorts
Dim b = {1S, 2S, 3S}
' array of shorts whose type is taken from the context
Dim c As Short() = {1, 2, 3}
' array of type Integer(,)
Dim d = {{1, 0}, {0, 1}}
' jagged array of rank ()()
Dim e = {({1, 0}), ({0, 1})}
' error: inconsistent rank
Dim f = {{1}, {2, 3}}
' error: inconsistent rank
Dim g = {1, {2}}
El formato y los requisitos para el inicializador de colección en un literal de matriz son exactamente los mismos que para el inicializador de colección en una expresión de creación de matriz.
Nota. Un literal de matriz no crea la matriz en y de sí misma; en su lugar, es la reclasificación de la expresión en un valor que hace que se cree la matriz. Por ejemplo, la conversión CType(new Integer() {1,2,3}, Short()) no es posible porque no hay ninguna conversión de Integer() a Short(); pero la expresión CType({1,2,3},Short()) es posible porque primero vuelve a clasificar el literal de matriz en la expresión New Short() {1,2,3}de creación de matriz .
expresiones de Delegate-Creation
Se usa una expresión de creación de delegados para crear una nueva instancia de un tipo delegado. El argumento de una expresión de creación de delegados debe ser una expresión clasificada como puntero de método o un método lambda.
Si el argumento es un puntero de método, uno de los métodos a los que hace referencia el puntero de método debe ser aplicable a la firma del tipo delegado. Un método M es aplicable a un tipo D de delegado si:
MnoPartiales o tiene un cuerpo.DyMson funciones, oDes una subrutina.MyDtienen el mismo número de parámetros.Los tipos de parámetro de
Mcada uno tienen una conversión del tipo del tipo de parámetro correspondiente deDy sus modificadores (es decirByRef, ,ByVal) coinciden.El tipo de valor devuelto de
M, si existe, tiene una conversión al tipo de valor devuelto deD.
Si el puntero de método hace referencia a un acceso enlazado en tiempo de ejecución, se supone que el acceso enlazado en tiempo de ejecución es para una función que tiene el mismo número de parámetros que el tipo de delegado.
Si no se usa la semántica estricta y solo hay un método al que hace referencia el puntero del método, pero no es aplicable debido al hecho de que no tiene parámetros y el tipo de delegado sí, el método se considera aplicable y los parámetros o el valor devuelto simplemente se omiten. Por ejemplo:
Delegate Sub F(x As Integer)
Module Test
Sub M()
End Sub
Sub Main()
' Valid
Dim x As F = AddressOf M
End Sub
End Module
Nota. Esta relajación solo se permite cuando no se usa semántica estricta debido a los métodos de extensión. Dado que los métodos de extensión solo se consideran si no se aplica un método normal, es posible que un método de instancia sin parámetros oculte un método de extensión con parámetros para la construcción del delegado.
Si se aplica más de un método al que hace referencia el puntero de método al tipo delegado, la resolución de sobrecarga se usa para elegir entre los métodos candidatos. Los tipos de los parámetros para el delegado se usan como tipos de los argumentos con fines de resolución de sobrecarga. Si ningún candidato de método es más aplicable, se produce un error en tiempo de compilación. En el ejemplo siguiente, la variable local se inicializa con un delegado que hace referencia al segundo Square método porque ese método es más aplicable a la firma y el tipo de valor devuelto de DoubleFunc.
Delegate Function DoubleFunc(x As Double) As Double
Module Test
Function Square(x As Single) As Single
Return x * x
End Function
Function Square(x As Double) As Double
Return x * x
End Function
Sub Main()
Dim a As New DoubleFunc(AddressOf Square)
End Sub
End Module
Si el segundo Square método no estuviera presente, se habría elegido el primer Square método. Si el entorno de compilación especifica una semántica estricta o Option Strict, se produce un error en tiempo de compilación si el método más específico al que hace referencia el puntero de método es más estrecho que la firma del delegado. Un método M se considera más estrecho que un tipo D de delegado si:
Un tipo de parámetro de
Mtiene una conversión de ampliación al tipo de parámetro correspondiente deD.O bien, el tipo de valor devuelto, si existe, de tiene una conversión de
Mrestricción al tipo de valor devuelto deD.
Si los argumentos de tipo están asociados al puntero de método, solo se tienen en cuenta los métodos con el mismo número de argumentos de tipo. Si no hay argumentos de tipo asociados con el puntero de método, se usa la inferencia de tipos al hacer coincidir firmas con un método genérico. A diferencia de otras inferencias de tipos normales, el tipo de valor devuelto del delegado se usa al inferir argumentos de tipo, pero los tipos devueltos todavía no se tienen en cuenta al determinar la sobrecarga menos genérica. En el ejemplo siguiente se muestran ambas maneras de proporcionar un argumento de tipo a una expresión de creación de delegados:
Delegate Function D(s As String, i As Integer) As Integer
Delegate Function E() As Integer
Module Test
Public Function F(Of T)(s As String, t1 As T) As T
End Function
Public Function G(Of T)() As T
End Function
Sub Main()
Dim d1 As D = AddressOf f(Of Integer) ' OK, type arg explicit
Dim d2 As D = AddressOf f ' OK, type arg inferred
Dim e1 As E = AddressOf g(Of Integer) ' OK, type arg explicit
Dim e2 As E = AddressOf g ' OK, infer from return
End Sub
End Module
En el ejemplo anterior, se creó una instancia de un tipo de delegado no genérico mediante un método genérico. También es posible crear una instancia de un tipo delegado construido mediante un método genérico. Por ejemplo:
Delegate Function Predicate(Of U)(u1 As U, u2 As U) As Boolean
Module Test
Function Compare(Of T)(t1 As List(of T), t2 As List(of T)) As Boolean
...
End Function
Sub Main()
Dim p As Predicate(Of List(Of Integer))
p = AddressOf Compare(Of Integer)
End Sub
End Module
Si el argumento para la expresión de creación de delegados es un método lambda, el método lambda debe ser aplicable a la firma del tipo delegado. Un método L lambda es aplicable a un tipo D de delegado si:
Si
Ltiene parámetros,Dtiene el mismo número de parámetros. (SiLno tiene parámetros, se omiten los parámetros deD).Los tipos de parámetro de
Lcada uno tienen una conversión al tipo del tipo de parámetro correspondiente deDy sus modificadores (es decirByRef, ,ByVal) coinciden.Si
Des una función, el tipo de valor devuelto deLtiene una conversión al tipo de valor devuelto deD. (SiDes una subrutina, se omite el valor devuelto deL).
Si se omite el tipo de parámetro de L un parámetro de , se deduce el tipo del parámetro correspondiente en D ; si el parámetro de tiene modificadores de L nombre que aceptan valores NULL o matriz, se produce un error en tiempo de compilación. Una vez que todos los tipos de parámetro de L están disponibles, se deduce el tipo de la expresión en el método lambda. Por ejemplo:
Delegate Function F(x As Integer, y As Long) As Long
Module Test
Sub Main()
' b inferred to Integer, c and return type inferred to Long
Dim a As F = Function(b, c) b + c
' e and return type inferred to Integer, f inferred to Long
Dim d As F = Function(e, f) e + CInt(f)
End Sub
End Module
En algunas situaciones en las que la firma de delegado no coincide exactamente con el método lambda o la firma del método, es posible que .NET Framework no admita la creación del delegado de forma nativa. En esa situación, se usa una expresión de método lambda para buscar coincidencias con los dos métodos. Por ejemplo:
Delegate Function IntFunc(x As Integer) As Integer
Module Test
Function SquareString(x As String) As String
Return CInt(x) * CInt(x)
End Function
Sub Main()
' The following two lines are equivalent
Dim a As New IntFunc(AddressOf SquareString)
Dim b As New IntFunc( _
Function(x As Integer) CInt(SquareString(CStr(x))))
End Sub
End Module
El resultado de una expresión de creación de delegados es una instancia de delegado que hace referencia al método coincidente con la expresión de destino asociada (si existe) desde la expresión de puntero de método. Si la expresión de destino se escribe como un tipo de valor, el tipo de valor se copia en el montón del sistema porque un delegado solo puede apuntar a un método de un objeto en el montón. El método y el objeto al que hace referencia un delegado permanecen constantes durante toda la duración del delegado. Es decir, no es posible cambiar el destino o el objeto de un delegado una vez creado.
Expresiones Object-Creation anónimas
Una expresión de creación de objetos con inicializadores de miembro también puede omitir completamente el nombre de tipo.
AnonymousObjectCreationExpression
: 'New' ObjectMemberInitializer
;
En ese caso, se construye un tipo anónimo en función de los tipos y nombres de los miembros inicializados como parte de la expresión. Por ejemplo:
Module Test
Sub Main()
Dim Customer = New With { .Name = "John Smith", .Age = 34 }
Console.WriteLine(Customer.Name)
End Sub
End Module
El tipo creado por una expresión de creación de objetos anónimo es una clase que no tiene nombre, hereda directamente de Objecty tiene un conjunto de propiedades con el mismo nombre que los miembros asignados a en la lista de inicializadores de miembros. El tipo de cada propiedad se deduce mediante las mismas reglas que la inferencia de tipo de variable local. Los tipos anónimos generados también invalidan ToString, devolviendo una representación de cadena de todos los miembros y sus valores. (El formato exacto de esta cadena está fuera del ámbito de esta especificación).
De forma predeterminada, las propiedades generadas por el tipo anónimo son de lectura y escritura. Es posible marcar una propiedad de tipo anónimo como de solo lectura mediante el Key modificador . El Key modificador especifica que el campo se puede usar para identificar de forma única el valor que representa el tipo anónimo. Además de hacer que la propiedad sea de solo lectura, también hace que el tipo anónimo invalide Equals e implemente la interfaz System.IEquatable(Of T) (rellenando el tipo anónimo para TGetHashCode ). Los miembros se definen de la siguiente manera:
Function Equals(obj As Object) As Boolean y Function Equals(val As T) As Boolean se implementan validando que las dos instancias tienen el mismo tipo y, a continuación, comparan cada Key miembro mediante Object.Equals. Si todos los Key miembros son iguales, Equals devuelve True; de lo contrario Equals , devuelve False.
Function GetHashCode() As Integer se implementa de forma que, si Equals es true para dos instancias del tipo anónimo, devolverá GetHashCode el mismo valor. El hash comienza con un valor de inicialización y, a continuación, para cada Key miembro, en orden multiplica el hash por 31 y agrega el Key valor hash del miembro (proporcionado por GetHashCode) si el miembro no es un tipo de referencia o un tipo de valor que acepta valores NULL con el valor de Nothing.
Por ejemplo, el tipo creado en la instrucción :
Dim zipState = New With { Key .ZipCode = 98112, .State = "WA" }
crea una clase que tiene un aspecto aproximado al siguiente (aunque la implementación exacta puede variar):
Friend NotInheritable Class $Anonymous1
Implements IEquatable(Of $Anonymous1)
Private ReadOnly _zipCode As Integer
Private _state As String
Public Sub New(zipCode As Integer, state As String)
_zipCode = zipcode
_state = state
End Sub
Public ReadOnly Property ZipCode As Integer
Get
Return _zipCode
End Get
End Property
Public Property State As String
Get
Return _state
End Get
Set (value As Integer)
_state = value
End Set
End Property
Public Overrides Function Equals(obj As Object) As Boolean
Dim val As $Anonymous1 = TryCast(obj, $Anonymous1)
Return Equals(val)
End Function
Public Overloads Function Equals(val As $Anonymous1) As Boolean _
Implements IEquatable(Of $Anonymous1).Equals
If val Is Nothing Then
Return False
End If
If Not Object.Equals(_zipCode, val._zipCode) Then
Return False
End If
Return True
End Function
Public Overrides Function GetHashCode() As Integer
Dim hash As Integer = 0
hash = hash Xor _zipCode.GetHashCode()
Return hash
End Function
Public Overrides Function ToString() As String
Return "{ Key .ZipCode = " & _zipCode & ", .State = " & _state & " }"
End Function
End Class
Para simplificar la situación en la que se crea un tipo anónimo a partir de los campos de otro tipo, los nombres de campo se pueden inferir directamente desde expresiones en los casos siguientes:
Una expresión
xde nombre simple deduce el nombrex.Una expresión
x.yde acceso de miembro deduce el nombrey.Una expresión
x!yde búsqueda de diccionario deduce el nombrey.Una expresión de invocación o índice sin argumentos
x()deduce el nombrex.Una expresión
x.<y>de acceso de miembro XML ,x...<y>,x.@ydeduce el nombrey.Una expresión de acceso de miembro XML que es el destino de una expresión
x.<y>.zde acceso de miembro deduce el nombrez.Una expresión de acceso de miembro XML que es el destino de una expresión de invocación o índice sin argumentos
x.<y>.z()deduce el nombrez.Una expresión de acceso de miembro XML que es el destino de una expresión de invocación o índice
x.<y>(0)deduce el nombrey.
El inicializador se interpreta como una asignación de la expresión al nombre inferido. Por ejemplo, los inicializadores siguientes son equivalentes:
Class Address
Public Street As String
Public City As String
Public State As String
Public ZIP As String
End Class
Class C1
Sub Test(a As Address)
Dim cityState1 = New With { .City = a.City, .State = a.State }
Dim cityState2 = New With { a.City, a.State }
End Sub
End Class
Si se deduce un nombre de miembro que entra en conflicto con un miembro existente del tipo, como GetHashCode, se produce un error en tiempo de compilación. A diferencia de los inicializadores de miembros normales, las expresiones anónimas de creación de objetos no permiten que los inicializadores de miembros tengan referencias circulares o hacer referencia a un miembro antes de que se haya inicializado. Por ejemplo:
Module Test
Sub Main()
' Error: Circular references
Dim x = New With { .a = .b, .b = .a }
' Error: Referring to .b before it has been assigned to
Dim y = New With { .a = .b, .b = 10 }
' Error: Referring to .a before it has been assigned to
Dim z = New With { .a = .a }
End Sub
End Module
Si se producen dos expresiones de creación de clases anónimas dentro del mismo método y producen la misma forma resultante (si el orden de propiedad, los nombres de propiedad y los tipos de propiedad coinciden), ambos harán referencia a la misma clase anónima. El ámbito del método de una instancia o variable miembro compartida con un inicializador es el constructor en el que se inicializa la variable.
Nota. Es posible que un compilador elija unificar aún más tipos anónimos, como en el nivel de ensamblado, pero esto no se puede confiar en este momento.
Expresiones de conversión
Una expresión de conversión convierte una expresión en un tipo determinado. Expresiones de coerción específicas de palabras clave de conversión en los tipos primitivos. Tres palabras clave de conversión generales, CType, TryCast y DirectCast, convierten una expresión en un tipo.
CastExpression
: 'DirectCast' OpenParenthesis Expression Comma TypeName CloseParenthesis
| 'TryCast' OpenParenthesis Expression Comma TypeName CloseParenthesis
| 'CType' OpenParenthesis Expression Comma TypeName CloseParenthesis
| CastTarget OpenParenthesis Expression CloseParenthesis
;
CastTarget
: 'CBool' | 'CByte' | 'CChar' | 'CDate' | 'CDec' | 'CDbl' | 'CInt'
| 'CLng' | 'CObj' | 'CSByte' | 'CShort' | 'CSng' | 'CStr' | 'CUInt'
| 'CULng' | 'CUShort'
;
DirectCast y TryCast tienen comportamientos especiales. Por este motivo, solo admiten conversiones nativas. Además, el tipo de destino de una TryCast expresión no puede ser un tipo de valor. Los operadores de conversión definidos por el usuario no se consideran cuando DirectCast se usa o TryCast . (Nota. El conjunto de conversión y DirectCastTryCast la compatibilidad están restringidos porque implementan conversiones "CLR nativas". El propósito de DirectCast es proporcionar la funcionalidad de la instrucción "unbox", mientras que el propósito de TryCast es proporcionar la funcionalidad de la instrucción "isinst". Dado que se asignan a instrucciones CLR, admitir conversiones no admitidas directamente por CLR derrotarían el propósito previsto).
DirectCast convierte expresiones que se escriben de Object forma diferente a CType. Al convertir una expresión de tipo cuyo tipo Object en tiempo de ejecución es un tipo de valor primitivo, DirectCast produce una System.InvalidCastException excepción si el tipo especificado no es el mismo que el tipo en tiempo de ejecución de la expresión o System.NullReferenceException si la expresión se evalúa Nothingcomo . (Nota. Como se indicó anteriormente, DirectCast se asigna directamente a la instrucción CLR "unbox" cuando el tipo de la expresión es Object. En cambio, CType se convierte en una llamada a un asistente en tiempo de ejecución para realizar la conversión para que se puedan admitir conversiones entre tipos primitivos. En el caso de que una Object expresión se convierta en un tipo de valor primitivo y el tipo de la instancia real coincida con el tipo de destino, DirectCast será significativamente más rápido que CType).
TryCast convierte expresiones, pero no produce una excepción si la expresión no se puede convertir al tipo de destino. En su lugar, TryCast dará como resultado Nothing si la expresión no se puede convertir en tiempo de ejecución. (Nota. Como se indicó anteriormente, TryCast se asigna directamente a la instrucción CLR "isinst". Al combinar la comprobación de tipos y la conversión en una sola operación, TryCast puede ser más barato que hacer y TypeOf ... Is , a continuación, un CType.
Por ejemplo:
Interface ITest
Sub Test()
End Interface
Module Test
Sub Convert(o As Object)
Dim i As ITest = TryCast(o, ITest)
If i IsNot Nothing Then
i.Test()
End If
End Sub
End Module
Si no existe ninguna conversión desde el tipo de la expresión al tipo especificado, se produce un error en tiempo de compilación. De lo contrario, la expresión se clasifica como un valor y el resultado es el valor generado por la conversión.
Expresiones de operador
Hay dos tipos de operadores.
Los operadores unarios toman un operando y usan la notación de prefijo (por ejemplo, -x).
Los operadores binarios toman dos operandos y usan la notación infijo (por ejemplo, x + y). Con la excepción de los operadores relacionales, que siempre dan como resultado Boolean, un operador definido para un tipo determinado da como resultado ese tipo. Los operandos a un operador siempre deben clasificarse como un valor; el resultado de una expresión de operador se clasifica como un valor.
OperatorExpression
: ArithmeticOperatorExpression
| RelationalOperatorExpression
| LikeOperatorExpression
| ConcatenationOperatorExpression
| ShortCircuitLogicalOperatorExpression
| LogicalOperatorExpression
| ShiftOperatorExpression
| AwaitOperatorExpression
;
Precedencia y capacidad de asociación de operadores
Cuando una expresión contiene varios operadores binarios, la precedencia de los operadores controla el orden en el que se evalúan los operadores binarios individuales. Por ejemplo, la expresión x + y * z se evalúa como x + (y * z) porque el * operador tiene mayor prioridad que el + operador. En la tabla siguiente se enumeran los operadores binarios en orden descendente de precedencia:
| Categoría | Operadores |
|---|---|
| Primario | Todas las expresiones que no son de operador |
| Esperar | Await |
| Exponenciación | ^ |
| Negación unaria |
+, - |
| Multiplicativo |
*, / |
| División de enteros | \ |
| Módulo | Mod |
| Aditivo |
+, - |
| Concatenación | & |
| Shift |
<<, >> |
| Relacional |
=, <>, <, >, <=, >=, Like, IsIsNot |
| NOT lógico | Not |
| Y lógico |
And, AndAlso |
| O lógico |
Or, OrElse |
| XOR lógico | Xor |
Cuando una expresión contiene dos operadores con la misma precedencia, la asociatividad de los operadores controla el orden en el que se realizan las operaciones. Todos los operadores binarios son asociativos a la izquierda, lo que significa que las operaciones se realizan de izquierda a derecha. La precedencia y la asociatividad se pueden controlar mediante expresiones paréntesis.
Operandos de objeto
Además de los tipos normales admitidos por cada operador, todos los operadores admiten operandos de tipo Object. Los operadores aplicados a Object operandos se controlan de forma similar a las llamadas a método realizadas en Object valores: se puede elegir una llamada de método enlazada en tiempo de ejecución, en cuyo caso el tipo de tiempo de ejecución de los operandos, en lugar del tipo en tiempo de compilación, determina la validez y el tipo de la operación. Si el entorno de compilación especifica una semántica estricta o Option Strict, los operadores con operandos de tipo Object provocan un error en tiempo de compilación, excepto los TypeOf...Isoperadores , Is y IsNot .
Cuando la resolución del operador determina que una operación debe realizarse en tiempo de ejecución, el resultado de la operación es el resultado de aplicar el operador a los tipos de operando si los tipos en tiempo de ejecución de los operandos son tipos admitidos por el operador. El valor Nothing se trata como el valor predeterminado del tipo del otro operando en una expresión de operador binario. En una expresión de operador unario, o si ambos operandos están Nothing en una expresión de operador binario, el tipo de la operación es Integer o el único tipo de resultado del operador, si el operador no da como Integerresultado . El resultado de la operación siempre se devuelve a Object. Si los tipos de operando no tienen ningún operador válido, se produce una System.InvalidCastException excepción. Las conversiones en tiempo de ejecución se realizan sin tener en cuenta si son implícitas o explícitas.
Si el resultado de una operación binaria numérica produciría una excepción de desbordamiento (independientemente de si la comprobación de desbordamiento de enteros está activada o desactivada), el tipo de resultado se promueve al siguiente tipo numérico más amplio, si es posible. Por ejemplo, considere el código siguiente:
Module Test
Sub Main()
Dim o As Object = CObj(CByte(2)) * CObj(CByte(255))
Console.WriteLine(o.GetType().ToString() & " = " & o)
End Sub
End Module
Imprime el resultado siguiente:
System.Int16 = 512
Si no hay ningún tipo numérico más amplio disponible para contener el número, se produce una System.OverflowException excepción.
Resolución de operadores
Dado un tipo de operador y un conjunto de operandos, la resolución del operador determina qué operador se va a usar para los operandos. Al resolver operadores, los operadores definidos por el usuario se considerarán primero mediante los pasos siguientes:
En primer lugar, se recopilan todos los operadores candidatos. Los operadores candidatos son todos los operadores definidos por el usuario del tipo de operador concreto en el tipo de origen y todos los operadores definidos por el usuario del tipo determinado en el tipo de destino. Si el tipo de origen y el tipo de destino están relacionados, los operadores comunes solo se consideran una vez.
A continuación, la resolución de sobrecarga se aplica a los operadores y operandos para seleccionar el operador más específico. En el caso de los operadores binarios, esto puede dar lugar a una llamada enlazada en tiempo de ejecución.
Al recopilar los operadores candidatos para un tipo T?, se usan en su lugar los operadores de tipo T . También se levantan cualquiera de Tlos operadores definidos por el usuario que implican solo tipos de valor que no aceptan valores NULL. Un operador elevado usa la versión que acepta valores NULL de cualquier tipo de valor, con la excepción de los tipos devueltos de IsTrue y IsFalse (que deben ser Boolean). Los operadores liftados se evalúan convirtiendo los operandos en su versión que no acepta valores NULL, evaluando el operador definido por el usuario y, a continuación, convirtiendo el tipo de resultado en su versión que acepta valores NULL. Si el operando ether es Nothing, el resultado de la expresión es un valor de Nothing tipado como la versión que acepta valores NULL del tipo de resultado. Por ejemplo:
Structure T
...
End Structure
Structure S
Public Shared Operator +(ByVal op1 As S, ByVal op2 As T) As T
...
End Operator
End Structure
Module Test
Sub Main()
Dim x As S?
Dim y, z As T?
' Valid, as S + T = T is lifted to S? + T? = T?
z = x + y
End Sub
End Module
Si el operador es un operador binario y uno de los operandos es de tipo de referencia, el operador también se levanta, pero cualquier enlace al operador produce un error. Por ejemplo:
Structure S1
Public F1 As Integer
Public Shared Operator +(left As S1, right As String) As S1
...
End Operator
End Structure
Module Test
Sub Main()
Dim a? As S1
Dim s As String
' Error: '+' is not defined for S1? and String
a = a + s
End Sub
End Module
Nota. Esta regla existe porque se ha tenido en cuenta si se desea agregar tipos de referencia de propagación null en una versión futura, en cuyo caso el comportamiento en el caso de operadores binarios entre los dos tipos cambiaría.
Al igual que con las conversiones, los operadores definidos por el usuario siempre se prefieren sobre los operadores elevados.
Al resolver operadores sobrecargados, puede haber diferencias entre las clases definidas en Visual Basic y las definidas en otros lenguajes:
En otros lenguajes,
Not,AndyOrse pueden sobrecargar como operadores lógicos y operadores bit a bit. Tras la importación desde un ensamblado externo, cualquiera de los formularios se acepta como una sobrecarga válida para estos operadores. Sin embargo, para un tipo que define operadores lógicos y bit a bit, solo se considerará la implementación bit a bit.En otros lenguajes,
>>y<<se pueden sobrecargar como operadores firmados y operadores sin signo. Tras la importación desde un ensamblado externo, cualquiera de los formularios se acepta como una sobrecarga válida. Sin embargo, para un tipo que define operadores firmados y sin firmar, solo se considerará la implementación firmada.Si ningún operador definido por el usuario es más específico de los operandos, se considerarán operadores intrínsecos. Si no se define ningún operador intrínseco para los operandos y cualquiera de los operandos tiene el tipo Object, el operador se resolverá en tiempo de ejecución; de lo contrario, se produce un error en tiempo de compilación.
En versiones anteriores de Visual Basic, si había exactamente un operando de tipo Object y ningún operador definido por el usuario aplicable, y ningún operador intrínseco aplicable, se produjo un error. A partir de Visual Basic 11, ahora se resuelve en tiempo de ejecución. Por ejemplo:
Module Module1
Sub Main()
Dim p As Object = Nothing
Dim U As New Uri("http://www.microsoft.com")
Dim j = U * p ' is now resolved late-bound
End Sub
End Module
Un tipo T que tiene un operador intrínseco también define ese mismo operador para T?. El resultado del operador en T? será el mismo que para T, salvo que si alguno de los operandos es Nothing, el resultado del operador será Nothing (es decir, el valor null se propaga). Para resolver el tipo de una operación, ? se quita de los operandos que los tienen, se determina el tipo de la operación y ? se agrega al tipo de la operación si alguno de los operandos eran tipos de valor que aceptan valores NULL. Por ejemplo:
Dim v1? As Integer = 10
Dim v2 As Long = 20
' Type of operation will be Long?
Console.WriteLine(v1 + v2)
Cada operador muestra los tipos intrínsecos para los que se define y el tipo de la operación realizada según los tipos de operando. El resultado del tipo de una operación intrínseca sigue estas reglas generales:
Si todos los operandos son del mismo tipo y el operador se define para el tipo, no se produce ninguna conversión y se usa el operador para ese tipo.
Cualquier operando cuyo tipo no está definido para el operador se convierte mediante los pasos siguientes y el operador se resuelve con los nuevos tipos:
El operando se convierte en el siguiente tipo más ancho que se define para el operador y el operando y al que se convierte implícitamente.
Si no hay ningún tipo de este tipo, el operando se convierte en el siguiente tipo más estrecho definido para el operador y el operando y al que se convierte implícitamente.
Si no hay ningún tipo de este tipo o no se puede producir la conversión, se produce un error en tiempo de compilación.
De lo contrario, los operandos se convierten en el más amplio de los tipos de operando y se usa el operador para ese tipo. Si el tipo de operando más estrecho no se puede convertir implícitamente al tipo de operador más amplio, se produce un error en tiempo de compilación.
A pesar de estas reglas generales, sin embargo, hay una serie de casos especiales resaltados en las tablas de resultados del operador.
Nota. Por motivos de formato, las tablas de tipo de operador abrevian los nombres predefinidos a sus dos primeros caracteres. Por lo tanto, "By" es Byte, "UI" es UInteger, "St" es String, etc. "Err" significa que no hay ninguna operación definida para los tipos de operando especificados.
Operadores aritméticos
Los *operadores , /, \, Mod^, , +y - son los operadores aritméticos.
ArithmeticOperatorExpression
: UnaryPlusExpression
| UnaryMinusExpression
| AdditionOperatorExpression
| SubtractionOperatorExpression
| MultiplicationOperatorExpression
| DivisionOperatorExpression
| ModuloOperatorExpression
| ExponentOperatorExpression
;
Las operaciones aritméticas de punto flotante se pueden realizar con mayor precisión que el tipo de resultado de la operación. Por ejemplo, algunas arquitecturas de hardware admiten un tipo de punto flotante "extendido" o "long double" con mayor rango y precisión que el Double tipo, y realizan implícitamente todas las operaciones de punto flotante con este tipo de mayor precisión. Las arquitecturas de hardware se pueden realizar para realizar operaciones de punto flotante con menos precisión solo a un costo excesivo en el rendimiento; en lugar de requerir que una implementación pierda el rendimiento y la precisión, Visual Basic permite usar el tipo de mayor precisión para todas las operaciones de punto flotante. Aparte de ofrecer resultados más precisos, esto rara vez tiene efectos medibles. Sin embargo, en expresiones del formulario x * y / z, donde la multiplicación produce un resultado que está fuera del Double intervalo, pero la división posterior devuelve el resultado temporal al Double intervalo, el hecho de que la expresión se evalúa en un formato de rango superior puede provocar que se produzca un resultado finito en lugar de infinito.
Operador Unary Plus
UnaryPlusExpression
: '+' Expression
;
El operador unario plus se define para los Bytetipos , SByteIntegerUIntegerLongULongShortUShort, , Single, , Doubley .Decimal
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob |
Operador unario menos
UnaryMinusExpression
: '-' Expression
;
El operador unario menos se define para los siguientes tipos:
SByte, Short, Integer y Long. El resultado se calcula restando el operando de cero. Si la comprobación de desbordamiento de enteros está activada y el valor del operando es el valor máximo negativo SByte, Short, Integero Long, se produce una System.OverflowException excepción. De lo contrario, si el valor del operando es el valor máximo negativo SByte, ShortInteger, o Long, el resultado es ese mismo valor y no se notifica el desbordamiento.
Single y Double. El resultado es el valor del operando con su signo invertido, incluidos los valores 0 e Infinito. Si el operando es NaN, el resultado también es NaN.
Decimal. El resultado se calcula restando el operando de cero.
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob |
Operador de suma
El operador de suma calcula la suma de los dos operandos.
AdditionOperatorExpression
: Expression '+' LineTerminator? Expression
;
El operador de suma se define para los siguientes tipos:
Byte,SByte,UShort,Short,UInteger,Integer,ULong, yLong. Si la comprobación de desbordamiento de enteros está activada y la suma está fuera del intervalo del tipo de resultado, se produce unaSystem.OverflowExceptionexcepción. De lo contrario, no se notifican los desbordamientos y se descartan los bits significativos de orden alto del resultado.SingleyDouble. La suma se calcula según las reglas de la aritmética IEEE 754.Decimal. Si el valor resultante es demasiado grande para representar en formato decimal, se produce unaSystem.OverflowExceptionexcepción. Si el valor del resultado es demasiado pequeño para representar en formato decimal, el resultado es 0.String. Los dosStringoperandos se concatenan juntos.Date. ElSystem.DateTimetipo define operadores de suma sobrecargados. Dado queSystem.DateTimees equivalente al tipo intrínsecoDate, estos operadores también están disponibles en elDatetipo .
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob |
| SB | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |
| Por | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||
| Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||
| EE.UU. | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||
| En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||
| interfaz de usuario | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||
| Lo | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||||
| UL | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||||
| De | De | Si | Haz | Err | Err | Haz | Ob | |||||||||
| Si | Si | Haz | Err | Err | Haz | Ob | ||||||||||
| Sí | Haz | Err | Err | Haz | Ob | |||||||||||
| Da | C | Err | C | Ob | ||||||||||||
| Ch | C | C | Ob | |||||||||||||
| C | C | Ob | ||||||||||||||
| Ob | Ob |
Operador de resta
El operador de resta resta el segundo operando del primer operando.
SubtractionOperatorExpression
: Expression '-' LineTerminator? Expression
;
El operador de resta se define para los siguientes tipos:
Byte,SByte,UShort,Short,UInteger,Integer,ULong, yLong. Si la comprobación de desbordamiento de enteros está activada y la diferencia está fuera del intervalo del tipo de resultado, se produce unaSystem.OverflowExceptionexcepción. De lo contrario, no se notifican los desbordamientos y se descartan los bits significativos de orden alto del resultado.SingleyDouble. La diferencia se calcula según las reglas de la aritmética IEEE 754.Decimal. Si el valor resultante es demasiado grande para representar en formato decimal, se produce unaSystem.OverflowExceptionexcepción. Si el valor del resultado es demasiado pequeño para representar en formato decimal, el resultado es 0.Date. ElSystem.DateTimetipo define operadores de resta sobrecargados. Dado queSystem.DateTimees equivalente al tipo intrínsecoDate, estos operadores también están disponibles en elDatetipo .
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob |
| SB | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |
| Por | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||
| Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||
| EE.UU. | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||
| En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||
| interfaz de usuario | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||
| Lo | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||||
| UL | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||||
| De | De | Si | Haz | Err | Err | Haz | Ob | |||||||||
| Si | Si | Haz | Err | Err | Haz | Ob | ||||||||||
| Sí | Haz | Err | Err | Haz | Ob | |||||||||||
| Da | Err | Err | Err | Err | ||||||||||||
| Ch | Err | Err | Err | |||||||||||||
| C | Haz | Ob | ||||||||||||||
| Ob | Ob |
Operador de multiplicación
El operador de multiplicación calcula el producto de dos operandos.
MultiplicationOperatorExpression
: Expression '*' LineTerminator? Expression
;
El operador de multiplicación se define para los siguientes tipos:
Byte,SByte,UShort,Short,UInteger,Integer,ULong, yLong. Si la comprobación de desbordamiento de enteros está activada y el producto está fuera del intervalo del tipo de resultado, se produce unaSystem.OverflowExceptionexcepción. De lo contrario, no se notifican los desbordamientos y se descartan los bits significativos de orden alto del resultado.SingleyDouble. El producto se calcula según las reglas de la aritmética IEEE 754.Decimal. Si el valor resultante es demasiado grande para representar en formato decimal, se produce unaSystem.OverflowExceptionexcepción. Si el valor del resultado es demasiado pequeño para representar en formato decimal, el resultado es 0.
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob |
| SB | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |
| Por | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||
| Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||
| EE.UU. | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||
| En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||
| interfaz de usuario | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||
| Lo | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||||
| UL | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||||
| De | De | Si | Haz | Err | Err | Haz | Ob | |||||||||
| Si | Si | Haz | Err | Err | Haz | Ob | ||||||||||
| Sí | Haz | Err | Err | Haz | Ob | |||||||||||
| Da | Err | Err | Err | Err | ||||||||||||
| Ch | Err | Err | Err | |||||||||||||
| C | Haz | Ob | ||||||||||||||
| Ob | Ob |
Operadores de división
Los operadores de división calculan el cociente de dos operandos. Hay dos operadores de división: el operador de división normal (punto flotante) y el operador de división de enteros.
DivisionOperatorExpression
: FPDivisionOperatorExpression
| IntegerDivisionOperatorExpression
;
FPDivisionOperatorExpression
: Expression '/' LineTerminator? Expression
;
IntegerDivisionOperatorExpression
: Expression '\\' LineTerminator? Expression
;
El operador de división regular se define para los siguientes tipos:
SingleyDouble. El cociente se calcula según las reglas de aritmética IEEE 754.Decimal. Si el valor del operando derecho es cero, se produce unaSystem.DivideByZeroExceptionexcepción. Si el valor resultante es demasiado grande para representar en formato decimal, se produce unaSystem.OverflowExceptionexcepción. Si el valor del resultado es demasiado pequeño para representar en formato decimal, el resultado es cero. La escala del resultado, antes de cualquier redondeo, es la escala más cercana a la escala preferida que conservará un resultado igual al resultado exacto. La escala preferida es la escala del primer operando menos la escala del segundo operando.
Según las reglas de resolución de operadores normales, la división normal puramente entre operandos de tipos como Byte, Short, Integery Long provocaría que ambos operandos se conviertan al tipo Decimal. Sin embargo, al realizar la resolución del operador en el operador de división cuando ninguno de los tipos es Decimal, Double se considera más estrecho que Decimal. Esta convención se sigue porque Double la división es más eficaz que Decimal la división.
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | De | Si | Haz | Err | Err | Haz | Ob |
| SB | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | De | Si | Haz | Err | Err | Haz | Ob | |
| Por | Haz | Haz | Haz | Haz | Haz | Haz | Haz | De | Si | Haz | Err | Err | Haz | Ob | ||
| Sh | Haz | Haz | Haz | Haz | Haz | Haz | De | Si | Haz | Err | Err | Haz | Ob | |||
| EE.UU. | Haz | Haz | Haz | Haz | Haz | De | Si | Haz | Err | Err | Haz | Ob | ||||
| En | Haz | Haz | Haz | Haz | De | Si | Haz | Err | Err | Haz | Ob | |||||
| interfaz de usuario | Haz | Haz | Haz | De | Si | Haz | Err | Err | Haz | Ob | ||||||
| Lo | Haz | Haz | De | Si | Haz | Err | Err | Haz | Ob | |||||||
| UL | Haz | De | Si | Haz | Err | Err | Haz | Ob | ||||||||
| De | De | Si | Haz | Err | Err | Haz | Ob | |||||||||
| Si | Si | Haz | Err | Err | Haz | Ob | ||||||||||
| Sí | Haz | Err | Err | Haz | Ob | |||||||||||
| Da | Err | Err | Err | Err | ||||||||||||
| Ch | Err | Err | Err | |||||||||||||
| C | Haz | Ob | ||||||||||||||
| Ob | Ob |
El operador de división de enteros se define para Byte, , UShortShortUIntegerSByte, Integer, y . ULongLong Si el valor del operando derecho es cero, se produce una System.DivideByZeroException excepción. La división redondea el resultado hacia cero y el valor absoluto del resultado es el entero más grande posible que es menor que el valor absoluto del cociente de los dos operandos. El resultado es cero o positivo cuando los dos operandos tienen el mismo signo y cero o negativo cuando los dos operandos tienen signos opuestos. Si el operando izquierdo es el máximo negativo SByte, , ShortIntegero Long, y el operando derecho es -1, se produce un desbordamiento; si la comprobación de desbordamiento de enteros está activada, se produce una System.OverflowException excepción. De lo contrario, no se notifica el desbordamiento y el resultado es en su lugar el valor del operando izquierdo.
Nota. Dado que los dos operandos para los tipos sin signo siempre serán cero o positivos, el resultado siempre es cero o positivo. Como resultado de la expresión siempre será menor o igual que el mayor de los dos operandos, no es posible que se produzca un desbordamiento. Como tal comprobación de desbordamiento de enteros no se realiza para la división de enteros con dos enteros sin signo. El resultado es el tipo como el del operando izquierdo.
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | En | En | LO | LO | LO | LO | LO | LO | Err | Err | LO | Ob |
| SB | SB | Sh | Sh | En | En | LO | LO | LO | LO | LO | LO | Err | Err | LO | Ob | |
| Por | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | LO | LO | LO | Err | Err | LO | Ob | ||
| Sh | Sh | En | En | LO | LO | LO | LO | LO | LO | Err | Err | LO | Ob | |||
| EE.UU. | EE. UU. | En | Interfaz de usuario | LO | UL | LO | LO | LO | Err | Err | LO | Ob | ||||
| En | En | LO | LO | LO | LO | LO | LO | Err | Err | LO | Ob | |||||
| interfaz de usuario | Interfaz de usuario | LO | UL | LO | LO | LO | Err | Err | LO | Ob | ||||||
| Lo | LO | LO | LO | LO | LO | Err | Err | LO | Ob | |||||||
| UL | UL | LO | LO | LO | Err | Err | LO | Ob | ||||||||
| De | LO | LO | LO | Err | Err | LO | Ob | |||||||||
| Si | LO | LO | Err | Err | LO | Ob | ||||||||||
| Sí | LO | Err | Err | LO | Ob | |||||||||||
| Da | Err | Err | Err | Err | ||||||||||||
| Ch | Err | Err | Err | |||||||||||||
| C | LO | Ob | ||||||||||||||
| Ob | Ob |
Mod (operador)
El Mod operador (módulo) calcula el resto de la división entre dos operandos.
ModuloOperatorExpression
: Expression 'Mod' LineTerminator? Expression
;
El Mod operador se define para los siguientes tipos:
Byte,SByte,UShort,Short,UInteger,IntegeryULongLong. El resultado dex Mod yes el valor producido porx - (x \ y) * y. Siyes cero, se produce unaSystem.DivideByZeroExceptionexcepción. El operador de módulo nunca provoca un desbordamiento.SingleyDouble. El resto se calcula según las reglas de aritmética IEEE 754.Decimal. Si el valor del operando derecho es cero, se produce unaSystem.DivideByZeroExceptionexcepción. Si el valor resultante es demasiado grande para representar en formato decimal, se produce unaSystem.OverflowExceptionexcepción. Si el valor del resultado es demasiado pequeño para representar en formato decimal, el resultado es cero.
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Sh | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob |
| SB | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |
| Por | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||
| Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||
| EE.UU. | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||
| En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||
| interfaz de usuario | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||
| Lo | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||||
| UL | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||||
| De | De | Si | Haz | Err | Err | Haz | Ob | |||||||||
| Si | Si | Haz | Err | Err | Haz | Ob | ||||||||||
| Sí | Haz | Err | Err | Haz | Ob | |||||||||||
| Da | Err | Err | Err | Err | ||||||||||||
| Ch | Err | Err | Err | |||||||||||||
| C | Haz | Ob | ||||||||||||||
| Ob | Ob |
Operador de exponenciación
El operador de exponente calcula el primer operando elevado a la potencia del segundo operando.
ExponentOperatorExpression
: Expression '^' LineTerminator? Expression
;
El operador de exponenciación se define para el tipo Double. El valor se calcula según las reglas de la aritmética IEEE 754.
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob |
| SB | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob | |
| Por | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob | ||
| Sh | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob | |||
| EE.UU. | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob | ||||
| En | Haz | Haz | Haz | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob | |||||
| interfaz de usuario | Haz | Haz | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob | ||||||
| Lo | Haz | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob | |||||||
| UL | Haz | Haz | Haz | Haz | Err | Err | Haz | Ob | ||||||||
| De | Haz | Haz | Haz | Err | Err | Haz | Ob | |||||||||
| Si | Haz | Haz | Err | Err | Haz | Ob | ||||||||||
| Sí | Haz | Err | Err | Haz | Ob | |||||||||||
| Da | Err | Err | Err | Err | ||||||||||||
| Ch | Err | Err | Err | |||||||||||||
| C | Haz | Ob | ||||||||||||||
| Ob | Ob |
Operadores relacionales
Los operadores relacionales comparan valores entre sí. Los operadores de comparación son =, <>, <, >, <=y >=.
RelationalOperatorExpression
: Expression '=' LineTerminator? Expression
| Expression '<' '>' LineTerminator? Expression
| Expression '<' LineTerminator? Expression
| Expression '>' LineTerminator? Expression
| Expression '<' '=' LineTerminator? Expression
| Expression '>' '=' LineTerminator? Expression
;
Todos los operadores relacionales dan como resultado un Boolean valor.
Los operadores relacionales tienen el siguiente significado general:
El
=operador comprueba si los dos operandos son iguales.El
<>operador comprueba si los dos operandos no son iguales.El
<operador comprueba si el primer operando es menor que el segundo operando.El
>operador comprueba si el primer operando es mayor que el segundo operando.El
<=operador comprueba si el primer operando es menor o igual que el segundo operando.El
>=operador comprueba si el primer operando es mayor o igual que el segundo operando.
Los operadores relacionales se definen para los siguientes tipos:
Boolean. Los operadores comparan los valores de verdad de los dos operandos.Truese considera menor queFalse, que coincide con sus valores numéricos.Byte,SByte,UShort,Short,UInteger,Integer,ULong, yLong. Los operadores comparan los valores numéricos de los dos operandos enteros.SingleyDouble. Los operadores comparan los operandos según las reglas del estándar IEEE 754.Decimal. Los operadores comparan los valores numéricos de los dos operandos decimales.Date. Los operadores devuelven el resultado de comparar los dos valores de fecha y hora.Char. Los operadores devuelven el resultado de comparar los dos valores Unicode.String. Los operadores devuelven el resultado de comparar los dos valores mediante una comparación binaria o una comparación de texto. El entorno de compilación y laOption Compareinstrucción determinan la comparación utilizada. Una comparación binaria determina si el valor Unicode numérico de cada carácter de cada cadena es el mismo. Una comparación de texto realiza una comparación de texto Unicode basada en la referencia cultural actual que se usa en .NET Framework. Al realizar una comparación de cadenas, un valor NULL es equivalente al literal""de cadena .
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Bo | Ob |
| SB | SB | Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |
| Por | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||
| Sh | Sh | En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||
| EE.UU. | EE. UU. | En | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||
| En | En | LO | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||
| interfaz de usuario | Interfaz de usuario | LO | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||
| Lo | LO | De | De | Si | Haz | Err | Err | Haz | Ob | |||||||
| UL | UL | De | Si | Haz | Err | Err | Haz | Ob | ||||||||
| De | De | Si | Haz | Err | Err | Haz | Ob | |||||||||
| Si | Si | Haz | Err | Err | Haz | Ob | ||||||||||
| Sí | Haz | Err | Err | Haz | Ob | |||||||||||
| Da | Da | Err | Da | Ob | ||||||||||||
| Ch | Ch | C | Ob | |||||||||||||
| C | C | Ob | ||||||||||||||
| Ob | Ob |
Operador Like
El Like operador determina si una cadena coincide con un patrón determinado.
LikeOperatorExpression
: Expression 'Like' LineTerminator? Expression
;
El Like operador se define para el String tipo . El primer operando es la cadena que se está comparando y el segundo operando es el patrón con el que coincidir. El patrón se compone de caracteres Unicode. Las secuencias de caracteres siguientes tienen significados especiales:
El carácter
?coincide con cualquier carácter único.El carácter
*coincide con cero o más caracteres.El carácter
#coincide con cualquier dígito único (0-9).Una lista de caracteres rodeados por corchetes (
[ab...]) coincide con cualquier carácter único de la lista.Una lista de caracteres rodeados por corchetes y prefijos por un signo de exclamación (
[!ab...]) coincide con cualquier carácter único que no esté en la lista de caracteres.Dos caracteres de una lista de caracteres separados por un guión (
-) especifican un intervalo de caracteres Unicode empezando por el primer carácter y terminando con el segundo carácter. Si el segundo carácter no es posterior en el criterio de ordenación que el primer carácter, se produce una excepción en tiempo de ejecución. Un guión que aparece al principio o al final de una lista de caracteres especifica a sí mismo.
Para que coincidan con los caracteres especiales entre corchetes ([), signo de interrogación (?), signo de número (#) y asterisco (*), los corchetes deben incluirlos. No se puede usar el corchete derecho (]) dentro de un grupo para que coincida con sí mismo, pero se puede usar fuera de un grupo como carácter individual. La secuencia [] de caracteres se considera el literal ""de cadena .
Tenga en cuenta que las comparaciones de caracteres y el orden de las listas de caracteres dependen del tipo de comparaciones que se usan. Si se usan comparaciones binarias, las comparaciones de caracteres y el orden se basan en los valores Unicode numéricos. Si se usan comparaciones de texto, las comparaciones de caracteres y el orden se basan en la configuración regional actual que se usa en .NET Framework.
En algunos idiomas, los caracteres especiales del alfabeto representan dos caracteres independientes y viceversa. Por ejemplo, varios idiomas usan el carácter æ para representar los caracteres a y e cuando aparecen juntos, mientras que los caracteres ^ y O se pueden usar para representar el carácter Ô. Al usar comparaciones de texto, el Like operador reconoce estas equivalencias culturales. En ese caso, una aparición del carácter especial único en el patrón o cadena coincide con la secuencia de dos caracteres equivalente en la otra cadena. Del mismo modo, un único carácter especial en el patrón entre corchetes (por sí mismo, en una lista o en un intervalo) coincide con la secuencia de dos caracteres equivalente en la cadena y viceversa.
En una Like expresión en la que ambos operandos son Nothing o un operando tiene una conversión intrínseca a String y el otro operando es Nothing, Nothing se trata como si fuera el literal ""de cadena vacío .
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | C | C | C | C | C | C | C | C | C | C | C | C | C | C | C | Ob |
| SB | C | C | C | C | C | C | C | C | C | C | C | C | C | C | Ob | |
| Por | C | C | C | C | C | C | C | C | C | C | C | C | C | Ob | ||
| Sh | C | C | C | C | C | C | C | C | C | C | C | C | Ob | |||
| EE.UU. | C | C | C | C | C | C | C | C | C | C | C | Ob | ||||
| En | C | C | C | C | C | C | C | C | C | C | Ob | |||||
| interfaz de usuario | C | C | C | C | C | C | C | C | C | Ob | ||||||
| Lo | C | C | C | C | C | C | C | C | Ob | |||||||
| UL | C | C | C | C | C | C | C | Ob | ||||||||
| De | C | C | C | C | C | C | Ob | |||||||||
| Si | C | C | C | C | C | Ob | ||||||||||
| Sí | C | C | C | C | Ob | |||||||||||
| Da | C | C | C | Ob | ||||||||||||
| Ch | C | C | Ob | |||||||||||||
| C | C | Ob | ||||||||||||||
| Ob | Ob |
Operador de concatenación
ConcatenationOperatorExpression
: Expression '&' LineTerminator? Expression
;
El operador de concatenación se define para todos los tipos intrínsecos, incluidas las versiones que aceptan valores NULL de los tipos de valor intrínseco. También se define para la concatenación entre los tipos mencionados anteriormente y System.DBNull, que se trata como una Nothing cadena. El operador de concatenación convierte todos sus operandos Stringen ; en la expresión, todas las conversiones a String se consideran ampliación, independientemente de si se usan semánticas estrictas. Un System.DBNull valor se convierte en el literal Nothing escrito como String. Tipo de valor que acepta valores NULL cuyo valor también se Nothing convierte en el literal Nothing escrito como String, en lugar de producir un error en tiempo de ejecución.
Una operación de concatenación da como resultado una cadena que es la concatenación de los dos operandos en orden de izquierda a derecha. El valor Nothing se trata como si fuera el literal ""de cadena vacío .
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | C | C | C | C | C | C | C | C | C | C | C | C | C | C | C | Ob |
| SB | C | C | C | C | C | C | C | C | C | C | C | C | C | C | Ob | |
| Por | C | C | C | C | C | C | C | C | C | C | C | C | C | Ob | ||
| Sh | C | C | C | C | C | C | C | C | C | C | C | C | Ob | |||
| EE.UU. | C | C | C | C | C | C | C | C | C | C | C | Ob | ||||
| En | C | C | C | C | C | C | C | C | C | C | Ob | |||||
| interfaz de usuario | C | C | C | C | C | C | C | C | C | Ob | ||||||
| Lo | C | C | C | C | C | C | C | C | Ob | |||||||
| UL | C | C | C | C | C | C | C | Ob | ||||||||
| De | C | C | C | C | C | C | Ob | |||||||||
| Si | C | C | C | C | C | Ob | ||||||||||
| Sí | C | C | C | C | Ob | |||||||||||
| Da | C | C | C | Ob | ||||||||||||
| Ch | C | C | Ob | |||||||||||||
| C | C | Ob | ||||||||||||||
| Ob | Ob |
Operadores lógicos
Los Andoperadores , Not, Ory Xor se denominan operadores lógicos.
LogicalOperatorExpression
: 'Not' Expression
| Expression 'And' LineTerminator? Expression
| Expression 'Or' LineTerminator? Expression
| Expression 'Xor' LineTerminator? Expression
;
Los operadores lógicos se evalúan de la siguiente manera:
Para el
Booleantipo:Una operación lógica
Andse realiza en sus dos operandos.Una operación lógica
Notse realiza en su operando.Una operación lógica
Orse realiza en sus dos operandos.Una operación exclusiva
Orlógica se realiza en sus dos operandos.
Para
Byte, ,SByte,UShort,Short,IntegerUInteger,ULong, ,Longy todos los tipos enumerados, la operación especificada se realiza en cada bit de la representación binaria de los dos operandos:And: el bit de resultado es 1 si ambos bits son 1; de lo contrario, el bit de resultado es 0.Not: el bit de resultado es 1 si el bit es 0; de lo contrario, el bit de resultado es 1.Or: el bit de resultado es 1 si alguno de los bits es 1; de lo contrario, el bit de resultado es 0.Xor: el bit de resultado es 1 si alguno de los bits es 1, pero no ambos bits; de lo contrario, el bit de resultado es 0 (es decir, 1Xor0 = 1, 1Xor1 = 0).
Cuando los operadores lógicos
AndyOrse levantan para el tipoBoolean?, se extienden para abarcar la lógica booleana con tres valores como tales:Andse evalúa como true si ambos operandos son true; false si uno de los operandos es false;Nothingde otra manera.Orse evalúa como true si cualquiera de los operandos es true; false es que ambos operandos son false;Nothingde otra manera.
Por ejemplo:
Module Test
Sub Main()
Dim x?, y? As Boolean
x = Nothing
y = True
If x Or y Then
' Will execute
End If
End Sub
End Module
Nota. Idealmente, los operadores lógicos And y Or se levantarían mediante la lógica de tres valores para cualquier tipo que se pueda usar en una expresión booleana (es decir, un tipo que implementa IsTrue y IsFalse), de la misma manera que AndAlso y OrElse cortocircuito en cualquier tipo que se pueda usar en una expresión booleana. Desafortunadamente, la elevación de tres valores solo se aplica a Boolean?, por lo que los tipos definidos por el usuario que desean lógica de tres valores deben hacerlo manualmente definiendo And los operadores y Or para su versión que acepta valores NULL.
No es posible que estas operaciones se desborden. Los operadores de tipo enumerados realizan la operación bit a bit en el tipo subyacente del tipo enumerado, pero el valor devuelto es el tipo enumerado.
Tipo de operación no:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | SB | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | LO | LO | LO | Err | Err | LO | Ob |
Y, o bien, tipo de operación Xor:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | SB | Sh | Sh | En | En | LO | LO | LO | LO | LO | LO | Err | Err | Bo | Ob |
| SB | SB | Sh | Sh | En | En | LO | LO | LO | LO | LO | LO | Err | Err | LO | Ob | |
| Por | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | LO | LO | LO | Err | Err | LO | Ob | ||
| Sh | Sh | En | En | LO | LO | LO | LO | LO | LO | Err | Err | LO | Ob | |||
| EE.UU. | EE. UU. | En | Interfaz de usuario | LO | UL | LO | LO | LO | Err | Err | LO | Ob | ||||
| En | En | LO | LO | LO | LO | LO | LO | Err | Err | LO | Ob | |||||
| interfaz de usuario | Interfaz de usuario | LO | UL | LO | LO | LO | Err | Err | LO | Ob | ||||||
| Lo | LO | LO | LO | LO | LO | Err | Err | LO | Ob | |||||||
| UL | UL | LO | LO | LO | Err | Err | LO | Ob | ||||||||
| De | LO | LO | LO | Err | Err | LO | Ob | |||||||||
| Si | LO | LO | Err | Err | LO | Ob | ||||||||||
| Sí | LO | Err | Err | LO | Ob | |||||||||||
| Da | Err | Err | Err | Err | ||||||||||||
| Ch | Err | Err | Err | |||||||||||||
| C | LO | Ob | ||||||||||||||
| Ob | Ob |
Operadores lógicos de cortocircuito
Los AndAlso operadores y OrElse son las versiones de cortocircuito de los And operadores lógicos y Or .
ShortCircuitLogicalOperatorExpression
: Expression 'AndAlso' LineTerminator? Expression
| Expression 'OrElse' LineTerminator? Expression
;
Debido a su comportamiento de cortocircuito, el segundo operando no se evalúa en tiempo de ejecución si se conoce el resultado del operador después de evaluar el primer operando.
Los operadores lógicos de cortocircuito se evalúan de la siguiente manera:
Si el primer operando de una
AndAlsooperación se evalúa comoFalseo devuelve True de suIsFalseoperador, la expresión devuelve su primer operando. De lo contrario, se evalúa el segundo operando y se realiza una operación lógicaAnden los dos resultados.Si el primer operando de una
OrElseoperación se evalúa comoTrueo devuelve True de suIsTrueoperador, la expresión devuelve su primer operando. De lo contrario, el segundo operando se evalúa y se realiza una operación lógicaOren sus dos resultados.
Los AndAlso operadores y OrElse se definen para el tipo Booleano para cualquier tipo T que sobrecargue los operadores siguientes:
Public Shared Operator IsTrue(op As T) As Boolean
Public Shared Operator IsFalse(op As T) As Boolean
así como sobrecargar el operador o Or correspondienteAnd:
Public Shared Operator And(op1 As T, op2 As T) As T
Public Shared Operator Or(op1 As T, op2 As T) As T
Al evaluar los AndAlso operadores o OrElse , el primer operando solo se evalúa una vez y el segundo operando no se evalúa o se evalúa exactamente una vez. Por ejemplo, considere el código siguiente:
Module Test
Function TrueValue() As Boolean
Console.Write(" True")
Return True
End Function
Function FalseValue() As Boolean
Console.Write(" False")
Return False
End Function
Sub Main()
Console.Write("And:")
If FalseValue() And TrueValue() Then
End If
Console.WriteLine()
Console.Write("Or:")
If TrueValue() Or FalseValue() Then
End If
Console.WriteLine()
Console.Write("AndAlso:")
If FalseValue() AndAlso TrueValue() Then
End If
Console.WriteLine()
Console.Write("OrElse:")
If TrueValue() OrElse FalseValue() Then
End If
Console.WriteLine()
End Sub
End Module
Imprime el resultado siguiente:
And: False True
Or: True False
AndAlso: False
OrElse: True
En la forma levantada de los AndAlso operadores y OrElse , si el primer operando era un valor NULL Boolean?, el segundo operando se evalúa, pero el resultado siempre es un valor NULL Boolean?.
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob |
| SB | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob | |
| Por | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob | ||
| Sh | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob | |||
| EE.UU. | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob | ||||
| En | Bo | Bo | Bo | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob | |||||
| interfaz de usuario | Bo | Bo | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob | ||||||
| Lo | Bo | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob | |||||||
| UL | Bo | Bo | Bo | Bo | Err | Err | Bo | Ob | ||||||||
| De | Bo | Bo | Bo | Err | Err | Bo | Ob | |||||||||
| Si | Bo | Bo | Err | Err | Bo | Ob | ||||||||||
| Sí | Bo | Err | Err | Bo | Ob | |||||||||||
| Da | Err | Err | Err | Err | ||||||||||||
| Ch | Err | Err | Err | |||||||||||||
| C | Bo | Ob | ||||||||||||||
| Ob | Ob |
Operadores de desplazamiento
Los operadores binarios << y >> realizan operaciones de desplazamiento de bits.
ShiftOperatorExpression
: Expression '<' '<' LineTerminator? Expression
| Expression '>' '>' LineTerminator? Expression
;
Los operadores se definen para los Bytetipos , SByte, UShortShort, , UInteger, Integer, y LongULong . A diferencia de los otros operadores binarios, el tipo de resultado de una operación de desplazamiento se determina como si el operador fuera un operador unario con solo el operando izquierdo. El tipo del operando derecho debe convertirse implícitamente en Integer y no se usa para determinar el tipo de resultado de la operación.
El << operador hace que los bits del primer operando se cambien a la izquierda del número de lugares especificados por la cantidad de desplazamiento. Los bits de orden superior fuera del intervalo del tipo de resultado se descartan y las posiciones de bits vacías de orden bajo se rellenan en cero.
El >> operador hace que los bits del primer operando se cambien a la derecha el número de lugares especificados por la cantidad de desplazamiento. Los bits de orden bajo se descartan y las posiciones de bits vacías de orden alto se establecen en cero si el operando izquierdo es positivo o en uno si es negativo. Si el operando izquierdo es de tipo Byte, UShort, UIntegero ULong los bits de orden superior vacíos están llenos de cero.
Los operadores de desplazamiento desplazan los bits de la representación subyacente del primer operando por la cantidad del segundo operando. Si el valor del segundo operando es mayor que el número de bits del primer operando, o es negativo, la cantidad de desplazamiento se calcula como RightOperand And SizeMask donde SizeMask es:
| Tipo LeftOperand | SizeMask |
|---|---|
Byte, SByte |
7 (&H7) |
UShort, Short |
15 (&HF) |
UInteger, Integer |
31 (&H1F) |
ULong, Long |
63 (&H3F) |
Si la cantidad de desplazamiento es cero, el resultado de la operación es idéntico al valor del primer operando. No es posible que estas operaciones se desborden.
Tipo de operación:
| Bo | SB | Por | Sh | EE.UU. | En | interfaz de usuario | Lo | UL | De | Si | Sí | Da | Ch | C | Ob |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Sh | SB | Por | Sh | EE. UU. | En | Interfaz de usuario | LO | UL | LO | LO | LO | Err | Err | LO | Ob |
Expresiones booleanas
Una expresión booleana es una expresión que se puede probar para ver si es true o si es false.
BooleanExpression
: Expression
;
Un tipo T se puede usar en una expresión booleana si, en orden de preferencia:
TesBooleanoBoolean?Ttiene una conversión de ampliación aBooleanTtiene una conversión de ampliación aBoolean?Tdefine dos pseudo operadores yIsTrueIsFalse.Ttiene una conversión de restricción aBoolean?que no implica una conversión deBooleanaBoolean?.Ttiene una conversión de restricción aBoolean.
Nota. Es interesante tener en cuenta que, si Option Strict está desactivado, se aceptará una expresión que tiene una conversión de restricción sin Boolean un error en tiempo de compilación, pero el lenguaje seguirá preferiendo un IsTrue operador si existe. Esto se debe Option Strict a que solo cambia lo que es y no es aceptado por el lenguaje, y nunca cambia el significado real de una expresión. Por lo tanto, IsTrue siempre se debe preferir sobre una conversión de restricción, independientemente de Option Strict.
Por ejemplo, la siguiente clase no define una conversión de ampliación a Boolean. Como resultado, su uso en la If instrucción provoca una llamada al IsTrue operador .
Class MyBool
Public Shared Widening Operator CType(b As Boolean) As MyBool
...
End Operator
Public Shared Narrowing Operator CType(b As MyBool) As Boolean
...
End Operator
Public Shared Operator IsTrue(b As MyBool) As Boolean
...
End Operator
Public Shared Operator IsFalse(b As MyBool) As Boolean
...
End Operator
End Class
Module Test
Sub Main()
Dim b As New MyBool
If b Then Console.WriteLine("True")
End Sub
End Module
Si una expresión booleana se escribe como o se convierte en Boolean o Boolean?, es true si el valor es True y false en caso contrario.
De lo contrario, una expresión booleana llama al IsTrue operador y devuelve True si el operador devolvió True; de lo contrario, es false (pero nunca llama al IsFalse operador).
En el ejemplo siguiente, Integer tiene una conversión de restricción a Boolean, por lo que un valor NULL Integer? tiene una conversión de restricción a ambas Boolean? (que produce un valor NULL Boolean) y a Boolean (que produce una excepción). Se prefiere la conversión de restricción a Boolean? y, por tanto, el valor de "i" como expresión booleana es, por lo tanto False, .
Dim i As Integer? = Nothing
If i Then Console.WriteLine()
Expresiones lambda
Una expresión lambda define un método anónimo denominado método lambda. Los métodos lambda facilitan el paso de métodos "en línea" a otros métodos que toman tipos delegados.
LambdaExpression
: SingleLineLambda
| MultiLineLambda
;
SingleLineLambda
: LambdaModifier* 'Function' ( OpenParenthesis ParameterList? CloseParenthesis )? Expression
| 'Sub' ( OpenParenthesis ParameterList? CloseParenthesis )? Statement
;
MultiLineLambda
: MultiLineFunctionLambda
| MultiLineSubLambda
;
MultiLineFunctionLambda
: LambdaModifier? 'Function' ( OpenParenthesis ParameterList? CloseParenthesis )? ( 'As' TypeName )? LineTerminator
Block
'End' 'Function'
;
MultiLineSubLambda
: LambdaModifier? 'Sub' ( OpenParenthesis ParameterList? CloseParenthesis )? LineTerminator
Block
'End' 'Sub'
;
LambdaModifier
: 'Async' | 'Iterator'
;
El ejemplo:
Module Test
Delegate Function IntFunc(x As Integer) As Integer
Sub Apply(a() As Integer, func As IntFunc)
For index As Integer = 0 To a.Length - 1
a(index) = func(a(index))
Next index
End Sub
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Apply(a, Function(x As Integer) x * 2)
For Each value In a
Console.Write(value & " ")
Next value
End Sub
End Module
imprimirá:
2 4 6 8
Una expresión lambda comienza con los modificadores opcionales Async o Iterator, seguidos de la palabra clave Function o Sub y una lista de parámetros. Los parámetros de una expresión lambda no se pueden declarar Optional o ParamArray y no pueden tener atributos. A diferencia de los métodos normales, la omisión de un tipo de parámetro para un método lambda no deduce Objectautomáticamente . En su lugar, cuando se vuelve a clasificar un método lambda, los tipos de parámetro y ByRef modificadores omitidos se deducen del tipo de destino. En el ejemplo anterior, la expresión lambda podría haberse escrito como Function(x) x * 2y habría inferido el tipo de x para ser Integer cuando se usó el método lambda para crear una instancia del IntFunc tipo delegado. A diferencia de la inferencia de variables locales, si un parámetro de método lambda omite un tipo pero tiene un modificador de nombre que acepta valores NULL o una matriz, se produce un error en tiempo de compilación.
Una expresión lambda regular es una con ni Async modificadores ni Iterator .
Una expresión lambda de iterador es una con el Iterator modificador y sin Async modificador. Debe ser una función. Cuando se vuelve a clasificar a un valor, solo se puede reclasificar a un valor de tipo delegado cuyo tipo de valor devuelto es IEnumerator, o IEnumerable, o IEnumerator(Of T)IEnumerable(Of T) para algunos T, y que no tiene parámetros ByRef.
Una expresión lambda asincrónica es una con el Async modificador y sin Iterator modificador. Una sub lambda asincrónica solo se puede reclasificar a un valor de tipo sub delegado sin parámetros ByRef. Una expresión lambda de función asincrónica solo se puede reclasificar a un valor de tipo delegado de función cuyo tipo de valor devuelto es Task o Task(Of T) para algunos T, y que no tiene parámetros ByRef.
Las expresiones lambda pueden ser de una sola línea o de varias líneas. Las expresiones lambda de una sola línea Function contienen una expresión única que representa el valor devuelto por el método lambda. Las expresiones lambda de una sola línea Sub contienen una sola instrucción sin cerrar StatementTerminator. Por ejemplo:
Module Test
Sub Do(a() As Integer, action As Action(Of Integer))
For index As Integer = 0 To a.Length - 1
action(a(index))
Next index
End Sub
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Do(a, Sub(x As Integer) Console.WriteLine(x))
End Sub
End Module
Las construcciones lambda de una sola línea enlazan menos estrechamente que todas las demás expresiones e instrucciones. Por lo tanto, por ejemplo, "Function() x + 5" es equivalente a " enFunction() (x+5)" lugar de "(Function() x) + 5". Para evitar ambigüedad, es posible que una expresión lambda de una sola línea Sub no contenga una instrucción Dim ni una instrucción de declaración de etiqueta. Además, a menos que esté entre paréntesis, una expresión lambda de una sola línea Sub no puede ir seguida inmediatamente de dos puntos ":", un operador de acceso de miembro ".", un operador de acceso de miembro de diccionario "!" o un paréntesis abierto "(". No puede contener ninguna instrucción de bloque (With, SyncLock, If...EndIf, While, For, , UsingDo) ni OnErrorResume.
Nota. En la expresión Function(i) x=ilambda , el cuerpo se interpreta como una expresión (que comprueba si x y i son iguales). Pero en la expresión Sub(i) x=ilambda , el cuerpo se interpreta como una instrucción (que asigna i a x).
Una expresión lambda de varias líneas contiene un bloque de instrucciones y debe terminar con una instrucción adecuada End (es decir, End Function o End Sub). Al igual que con los métodos normales, las instrucciones y las instrucciones de SubEnd un método Function lambda de varias líneas deben estar en sus propias líneas. Por ejemplo:
' Error: Function statement must be on its own line!
Dim x = Sub(x As Integer) : Console.WriteLine(x) : End Sub
' OK
Dim y = Sub(x As Integer)
Console.WriteLine(x)
End Sub
Las expresiones lambda de varias líneas Function pueden declarar un tipo de valor devuelto, pero no pueden colocar atributos en él. Si una expresión lambda de varias líneas Function no declara un tipo de valor devuelto, pero el tipo de valor devuelto se puede deducir del contexto en el que se usa la expresión lambda , se usa ese tipo de valor devuelto. De lo contrario, el tipo de valor devuelto de la función se calcula de la siguiente manera:
En una expresión lambda regular, el tipo de valor devuelto es el tipo dominante de las expresiones en todas las
Returninstrucciones del bloque de instrucciones.En una expresión lambda asincrónica, el tipo de valor devuelto es donde
TesTask(Of T)el tipo dominante de las expresiones en todas lasReturninstrucciones del bloque de instrucciones.En una expresión lambda de iterador, el tipo de valor devuelto es donde
TesIEnumerable(Of T)el tipo dominante de las expresiones en todas lasYieldinstrucciones del bloque de instrucciones.
Por ejemplo:
Function f(min As Integer, max As Integer) As IEnumerable(Of Integer)
If min > max Then Throw New ArgumentException()
Dim x = Iterator Function()
For i = min To max
Yield i
Next
End Function
' infers x to be a delegate with return type IEnumerable(Of Integer)
Return x()
End Function
En todos los casos, si no hay instrucciones Return (respectivamente Yield) o si no hay ningún tipo dominante entre ellos y se usa una semántica estricta, se produce un error en tiempo de compilación; de lo contrario, el tipo dominante es implícitamente Object.
Tenga en cuenta que el tipo de valor devuelto se calcula a partir de todas las Return instrucciones, incluso si no son accesibles. Por ejemplo:
' Return type is Double
Dim x = Function()
Return 10
Return 10.50
End Function
No hay ninguna variable de devolución implícita, ya que no hay ningún nombre para la variable.
Los bloques de instrucciones dentro de las expresiones lambda de varias líneas tienen las restricciones siguientes:
On ErrorNo se permiten instrucciones yResume, aunqueTryse permiten instrucciones .Las variables locales estáticas no se pueden declarar en expresiones lambda de varias líneas.
No es posible bifurcar o salir del bloque de instrucciones de una expresión lambda de varias líneas, aunque las reglas de bifurcación normales se aplican dentro de ella. Por ejemplo:
Label1: Dim x = Sub() ' Error: Cannot branch out GoTo Label1 ' OK: Wholly within the lamba. GoTo Label2: Label2: End Sub ' Error: Cannot branch in GoTo Label2
Una expresión lambda es aproximadamente equivalente a un método anónimo declarado en el tipo contenedor. El ejemplo inicial es aproximadamente equivalente a:
Module Test
Delegate Function IntFunc(x As Integer) As Integer
Sub Apply(a() As Integer, func As IntFunc)
For index As Integer = 0 To a.Length - 1
a(index) = func(a(index))
Next index
End Sub
Function $Lambda1(x As Integer) As Integer
Return x * 2
End Function
Sub Main()
Dim a() As Integer = { 1, 2, 3, 4 }
Apply(a, AddressOf $Lambda1)
For Each value In a
Console.Write(value & " ")
Next value
End Sub
End Module
Cierres
Las expresiones lambda tienen acceso a todas las variables del ámbito, incluidas las variables locales o los parámetros definidos en el método contenedor y las expresiones lambda. Cuando una expresión lambda hace referencia a una variable o parámetro local, la expresión lambda captura la variable a la que se hace referencia en un cierre. Un cierre es un objeto que reside en el montón en lugar de en la pila y, cuando se captura una variable, todas las referencias a la variable se redirigen al cierre. Esto permite que las expresiones lambda sigan haciendo referencia a variables y parámetros locales incluso después de que se complete el método contenedor. Por ejemplo:
Module Test
Delegate Function D() As Integer
Function M() As D
Dim x As Integer = 10
Return Function() x
End Function
Sub Main()
Dim y As D = M()
' Prints 10
Console.WriteLine(y())
End Sub
End Module
es aproximadamente equivalente a:
Module Test
Delegate Function D() As Integer
Class $Closure1
Public x As Integer
Function $Lambda1() As Integer
Return x
End Function
End Class
Function M() As D
Dim c As New $Closure1()
c.x = 10
Return AddressOf c.$Lambda1
End Function
Sub Main()
Dim y As D = M()
' Prints 10
Console.WriteLine(y())
End Sub
End Module
Un cierre captura una nueva copia de una variable local cada vez que entra en el bloque en el que se declara la variable local, pero la nueva copia se inicializa con el valor de la copia anterior, si existe. Por ejemplo:
Module Test
Delegate Function D() As Integer
Function M() As D()
Dim a(9) As D
For i As Integer = 0 To 9
Dim x
a(i) = Function() x
x += 1
Next i
Return a
End Function
Sub Main()
Dim y() As D = M()
For i As Integer = 0 To 9
Console.Write(y(i)() & " ")
Next i
End Sub
End Module
Huellas
1 2 3 4 5 6 7 8 9 10
En lugar de
9 9 9 9 9 9 9 9 9 9
Dado que los cierres deben inicializarse al entrar en un bloque, no se permite entrar GoTo en un bloque con un cierre desde fuera de ese bloque, aunque se permite entrar Resume en un bloque con un cierre. Por ejemplo:
Module Test
Sub Main()
Dim a = 10
If a = 10 Then
L1:
Dim x = Function() a
' Valid, source is within block
GoTo L2
L2:
End If
' ERROR: target is inside block with closure
GoTo L1
End Sub
End Module
Dado que no se pueden capturar en un cierre, lo siguiente no puede aparecer dentro de una expresión lambda:
Parámetros de referencia.
Expresiones de instancia (
Me,MyClass,MyBase), si el tipo deMeno es una clase.
Los miembros de una expresión de creación de tipos anónimos, si la expresión lambda forma parte de la expresión. Por ejemplo:
' Error: Lambda cannot refer to anonymous type field
Dim x = New With { .a = 12, .b = Function() .a }
ReadOnly variables de instancia en constructores de instancia o ReadOnly variables compartidas en constructores compartidos donde las variables se usan en un contexto que no es de valor. Por ejemplo:
Class C1
ReadOnly F1 As Integer
Sub New()
' Valid, doesn't modify F1
Dim x = Function() F1
' Error, tries to modify F1
Dim f = Function() ModifyValue(F1)
End Sub
Sub ModifyValue(ByRef x As Integer)
End Sub
End Class
Expresiones de consulta
Una expresión de consulta es una expresión que aplica una serie de operadores de consulta a los elementos de una colección consultable . Por ejemplo, la expresión siguiente toma una colección de Customer objetos y devuelve los nombres de todos los clientes en el estado de Washington:
Dim names = _
From cust In Customers _
Where cust.State = "WA" _
Select cust.Name
Una expresión de consulta debe comenzar con un From operador o Aggregate y puede terminar con cualquier operador de consulta. El resultado de una expresión de consulta se clasifica como un valor; el tipo de resultado de la expresión depende del tipo de resultado del último operador de consulta de la expresión.
QueryExpression
: FromOrAggregateQueryOperator QueryOperator*
;
FromOrAggregateQueryOperator
: FromQueryOperator
| AggregateQueryOperator
;
QueryOperator
: FromQueryOperator
| AggregateQueryOperator
| SelectQueryOperator
| DistinctQueryOperator
| WhereQueryOperator
| OrderByQueryOperator
| PartitionQueryOperator
| LetQueryOperator
| GroupByQueryOperator
| JoinOrGroupJoinQueryOperator
;
JoinOrGroupJoinQueryOperator
: JoinQueryOperator
| GroupJoinQueryOperator
;
Variables de intervalo
Algunos operadores de consulta presentan un tipo especial de variable denominada variable de rango. Las variables de rango no son variables reales; en su lugar, representan los valores individuales durante la evaluación de la consulta sobre las colecciones de entrada.
CollectionRangeVariableDeclarationList
: CollectionRangeVariableDeclaration ( Comma CollectionRangeVariableDeclaration )*
;
CollectionRangeVariableDeclaration
: Identifier ( 'As' TypeName )? 'In' LineTerminator? Expression
;
ExpressionRangeVariableDeclarationList
: ExpressionRangeVariableDeclaration ( Comma ExpressionRangeVariableDeclaration )*
;
ExpressionRangeVariableDeclaration
: Identifier ( 'As' TypeName )? Equals Expression
;
Las variables de intervalo se limitan desde el operador de consulta de introducción hasta el final de una expresión de consulta, o a un operador de consulta como, por Select ejemplo, que los oculta. Por ejemplo, en la consulta siguiente
Dim waCusts = _
From cust As Customer In Customers _
Where cust.State = "WA"
El From operador de consulta presenta una variable cust de rango con Customer tipo que representa a cada cliente de la Customers colección. A continuación, el operador de consulta siguiente Where hace referencia a la variable cust de intervalo de la expresión de filtro para determinar si se debe filtrar un cliente individual fuera de la colección resultante.
Hay dos tipos de variables de rango: variables de intervalo de colección y variables de intervalo de expresiones. Las variables de intervalo de recopilación toman sus valores de los elementos de las colecciones que se consultan. La expresión de colección de una declaración de variable de intervalo de colección debe clasificarse como un valor cuyo tipo es consultable. Si se omite el tipo de una variable de intervalo de colección, se deduce que es el tipo de elemento de la expresión de colección o Object si la expresión de colección no tiene un tipo de elemento (es decir, solo define un Cast método). Si la expresión de colección no se puede consultar (es decir, el tipo de elemento de la colección no se puede deducir), se producirá un error en tiempo de compilación.
Una variable de intervalo de expresiones es una variable de intervalo cuyo valor se calcula mediante una expresión en lugar de una colección. En el ejemplo siguiente, el Select operador de consulta presenta una variable de intervalo de expresiones denominada cityState calculada a partir de dos campos:
Dim cityStates = _
From cust As Customer In Customers _
Select cityState = cust.City & "," & cust.State _
Where cityState.Length() < 10
No se requiere una variable de intervalo de expresiones para hacer referencia a otra variable de rango, aunque dicha variable puede ser de valor dudoso. La expresión asignada a una variable de intervalo de expresiones debe clasificarse como un valor y debe convertirse implícitamente en el tipo de la variable de rango, si se proporciona.
Solo en un operador Let puede que una variable de intervalo de expresiones tenga su tipo especificado. En otros operadores, o si no se especifica su tipo, se usa la inferencia de tipos de variable local para determinar el tipo de la variable de intervalo.
Una variable de intervalo debe seguir las reglas para declarar variables locales con respecto al sombreado. Por lo tanto, una variable de rango no puede ocultar el nombre de una variable o parámetro local en el método envolvente u otra variable de intervalo (a menos que el operador de consulta oculte específicamente todas las variables de intervalo actuales en el ámbito).
Tipos consultables
Las expresiones de consulta se implementan mediante la traducción de la expresión en llamadas a métodos conocidos en un tipo de colección. Estos métodos bien definidos definen el tipo de elemento de la colección consultable, así como los tipos de resultado de los operadores de consulta ejecutados en la colección. Cada operador de consulta especifica el método o los métodos en los que el operador de consulta se traduce generalmente, aunque la traducción específica depende de la implementación. Los métodos se proporcionan en la especificación mediante un formato general similar al siguiente:
Function Select(selector As Func(Of T, R)) As CR
Lo siguiente se aplica a los métodos:
El método debe ser un miembro de instancia o extensión del tipo de colección y debe ser accesible.
El método puede ser genérico, siempre que sea posible deducir todos los argumentos de tipo.
El método se puede sobrecargar, en cuyo caso se usa la resolución de sobrecarga para determinar exactamente el método que se va a usar.
Se puede usar otro tipo de delegado en lugar del tipo delegado
Func, siempre que tenga la misma firma, incluido el tipo de valor devuelto, que el tipo coincidenteFunc.El tipo
System.Linq.Expressions.Expression(Of D)se puede usar en lugar del tipo de delegadoFunc, siempre queDsea un tipo delegado que tenga la misma firma, incluido el tipo de valor devuelto, que el tipo coincidenteFunc.El tipo
Trepresenta el tipo de elemento de la colección de entrada. Todos los métodos definidos por un tipo de colección deben tener el mismo tipo de elemento de entrada para que se pueda consultar el tipo de colección.El tipo
Srepresenta el tipo de elemento de la segunda colección de entrada en el caso de los operadores de consulta que realizan combinaciones.El tipo
Krepresenta un tipo de clave en el caso de los operadores de consulta que tienen un conjunto de variables de intervalo que actúan como claves.El tipo
Nrepresenta un tipo que se usa como tipo numérico (aunque todavía podría ser un tipo definido por el usuario y no un tipo numérico intrínseco).El tipo
Brepresenta un tipo que se puede usar en una expresión booleana.El tipo
Rrepresenta el tipo de elemento de la colección de resultados, si el operador de consulta genera una colección de resultados.Rdepende del número de variables de intervalo en el ámbito en la conclusión del operador de consulta. Si una sola variable de intervalo está en el ámbito, esRel tipo de esa variable de intervalo. En el ejemploDim custNames = From c In Customers Select c.Nameel resultado de la consulta será un tipo de colección con un tipo de elemento de
String. Si hay varias variables de rango en el ámbito,Res un tipo anónimo que contiene todas las variables de intervalo en el ámbito comoKeycampos. En el ejemplo:Dim custNames = From c In Customers, o In c.Orders Select Name = c.Name, ProductName = o.ProductNameel resultado de la consulta será un tipo de colección con un tipo de elemento de un tipo anónimo con una propiedad de solo lectura denominada
Namede tipoStringy una propiedad de solo lectura denominadaProductNamede tipoString.Dentro de una expresión de consulta, los tipos anónimos generados para contener variables de rango son transparentes, lo que significa que las variables de intervalo siempre están disponibles sin cualificación. Por ejemplo, en el ejemplo anterior, se podía acceder a las variables
cde intervalo yopodía tener acceso sin cualificación en el operador de consulta, aunque el tipo de elemento de laSelectcolección de entrada fuera un tipo anónimo.El tipo
CXrepresenta un tipo de colección, no necesariamente el tipo de colección de entrada, cuyo tipo de elemento es de algún tipoX.
Un tipo de colección consultable debe cumplir una de las siguientes condiciones, en orden de preferencia:
Debe definir un método conforme
Select.Debe tener uno de los métodos siguientes.
Function AsEnumerable() As CT Function AsQueryable() As CTal que se puede llamar para obtener una colección consultable. Si se proporcionan ambos métodos,
AsQueryablese prefiere sobreAsEnumerable.Debe tener un método
Function Cast(Of T)() As CTal que se puede llamar con el tipo de la variable de intervalo para generar una colección consultable.
Dado que la determinación del tipo de elemento de una colección se produce independientemente de una invocación de método real, no se puede determinar la aplicabilidad de métodos específicos. Por lo tanto, al determinar el tipo de elemento de una colección si hay métodos de instancia que coinciden con métodos conocidos, se omiten los métodos de extensión que coinciden con métodos conocidos.
La traducción del operador de consulta se produce en el orden en que se producen los operadores de consulta en la expresión. No es necesario que un objeto de colección implemente todos los métodos necesarios para todos los operadores de consulta, aunque todos los objetos de colección deben admitir al menos el Select operador de consulta. Si un método necesario no está presente, se produce un error en tiempo de compilación. Al enlazar nombres de método conocidos, los no métodos se omiten para el propósito de la herencia múltiple en interfaces y enlace de métodos de extensión, aunque se sigue aplicando la semántica de sombreado. Por ejemplo:
Class Q1
Public Function [Select](selector As Func(Of Integer, Integer)) As Q1
End Function
End Class
Class Q2
Inherits Q1
Public [Select] As Integer
End Class
Module Test
Sub Main()
Dim qs As New Q2()
' Error: Q2.Select still hides Q1.Select
Dim zs = From q In qs Select q
End Sub
End Module
Indexador de consultas predeterminado
Cada tipo de colección consultable cuyo tipo de elemento es T y aún no tiene una propiedad predeterminada se considera que tiene una propiedad predeterminada de la siguiente forma general:
Public ReadOnly Default Property Item(index As Integer) As T
Get
Return Me.ElementAtOrDefault(index)
End Get
End Property
Solo se puede hacer referencia a la propiedad predeterminada mediante la sintaxis de acceso a la propiedad predeterminada; No se puede hacer referencia a la propiedad predeterminada por nombre. Por ejemplo:
Dim customers As IEnumerable(Of Customer) = ...
Dim customerThree = customers(2)
' Error, no such property
Dim customerFour = customers.Item(4)
Si el tipo de colección no tiene un ElementAtOrDefault miembro, se producirá un error en tiempo de compilación.
From Query (Operador)
El From operador de consulta presenta una variable de intervalo de recopilación que representa los miembros individuales de una colección que se van a consultar.
FromQueryOperator
: LineTerminator? 'From' LineTerminator? CollectionRangeVariableDeclarationList
;
Por ejemplo, la expresión de consulta:
From c As Customer In Customers ...
puede considerarse equivalente a
For Each c As Customer In Customers
...
Next c
Cuando un From operador de consulta declara varias variables de intervalo de recopilación o no es el primer From operador de consulta de la expresión de consulta, cada nueva variable de intervalo de recopilación se combina entre sí al conjunto existente de variables de intervalo. El resultado es que la consulta se evalúa sobre el producto cruzado de todos los elementos de las colecciones combinadas. Por ejemplo, la expresión:
From c In Customers _
From e In Employees _
...
puede considerarse equivalente a:
For Each c In Customers
For Each e In Employees
...
Next e
Next c
y es exactamente equivalente a:
From c In Customers, e In Employees ...
Las variables de intervalo introducidas en los operadores de consulta anteriores se pueden usar en un operador de consulta posterior From . Por ejemplo, en la siguiente expresión de consulta, el segundo From operador de consulta hace referencia al valor de la primera variable de intervalo:
From c As Customer In Customers _
From o As Order In c.Orders _
Select c.Name, o
Solo se admiten varias variables de intervalo en un From operador de consulta o varios From operadores de consulta si el tipo de colección contiene uno o ambos de los métodos siguientes:
Function SelectMany(selector As Func(Of T, CR)) As CR
Function SelectMany(selector As Func(Of T, CS), _
resultsSelector As Func(Of T, S, R)) As CR
El código.
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs, y In ys ...
normalmente se traduce a
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.SelectMany( _
Function(x As Integer) ys, _
Function(x As Integer, y As Integer) New With {x, y})...
Nota.
From no es una palabra reservada.
Operador de consulta Join
El Join operador de consulta combina las variables de intervalo existentes con una nueva variable de intervalo de colección, lo que genera una sola colección cuyos elementos se han unido en función de una expresión de igualdad.
JoinQueryOperator
: LineTerminator? 'Join' LineTerminator? CollectionRangeVariableDeclaration
JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
;
JoinConditionList
: JoinCondition ( 'And' LineTerminator? JoinCondition )*
;
JoinCondition
: Expression 'Equals' LineTerminator? Expression
;
Por ejemplo:
Dim customersAndOrders = _
From cust In Customers _
Join ord In Orders On cust.ID Equals ord.CustomerID
La expresión de igualdad está más restringida que una expresión de igualdad regular:
Ambas expresiones deben clasificarse como un valor.
Ambas expresiones deben hacer referencia al menos a una variable de intervalo.
Una de las expresiones debe hacer referencia a la variable de intervalo declarada en el operador de consulta de combinación y esa expresión no debe hacer referencia a ninguna otra variable de intervalo.
Si los tipos de las dos expresiones no son exactamente del mismo tipo,
Si el operador de igualdad se define para los dos tipos, ambas expresiones se convierten implícitamente en él y no
Objectes , convierte ambas expresiones en ese tipo.De lo contrario, si hay un tipo dominante en el que ambas expresiones se pueden convertir implícitamente, convierta ambas expresiones en ese tipo.
De lo contrario, se produce un error en tiempo de compilación.
Las expresiones se comparan mediante valores hash (es decir, llamando a GetHashCode()) en lugar de usar operadores de igualdad para mejorar la eficacia. Un Join operador de consulta puede realizar varias combinaciones o condiciones de igualdad en el mismo operador. Un Join operador de consulta solo se admite si el tipo de colección contiene un método:
Function Join(inner As CS, _
outerSelector As Func(Of T, K), _
innerSelector As Func(Of S, K), _
resultSelector As Func(Of T, S, R)) As CR
El código.
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
Join y In ys On x Equals y _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.Join( _
ys, _
Function(x As Integer) x, _
Function(y As Integer) y, _
Function(x As Integer, y As Integer) New With {x, y})...
Note.Join, On y Equals no son palabras reservadas.
Let Query (Operador)
El Let operador de consulta presenta una variable de intervalo de expresiones. Esto permite calcular un valor intermedio una vez que se usará varias veces en operadores de consulta posteriores.
LetQueryOperator
: LineTerminator? 'Let' LineTerminator? ExpressionRangeVariableDeclarationList
;
Por ejemplo:
Dim taxedPrices = _
From o In Orders _
Let tax = o.Price * 0.088 _
Where tax > 3.50 _
Select o.Price, tax, total = o.Price + tax
puede considerarse equivalente a:
For Each o In Orders
Dim tax = o.Price * 0.088
...
Next o
Un Let operador de consulta solo se admite si el tipo de colección contiene un método:
Function Select(selector As Func(Of T, R)) As CR
El código.
Dim xs() As Integer = ...
Dim zs = From x In xs _
Let y = x * 10 _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim zs = _
xs.Select(Function(x As Integer) New With {x, .y = x * 10})...
Seleccionar operador de consulta
El Select operador de consulta es como el Let operador de consulta en que introduce variables de intervalo de expresiones; sin embargo, un Select operador de consulta oculta las variables de intervalo disponibles actualmente en lugar de agregarlas. Además, el tipo de una variable de intervalo de expresiones introducida por un Select operador de consulta siempre se deduce mediante reglas de inferencia de tipo de variable local; no se puede especificar un tipo explícito y, si no se puede deducir ningún tipo, se produce un error en tiempo de compilación.
SelectQueryOperator
: LineTerminator? 'Select' LineTerminator? ExpressionRangeVariableDeclarationList
;
Por ejemplo, en la consulta:
Dim smiths = _
From cust In Customers _
Select name = cust.name _
Where name.EndsWith("Smith")
el Where operador de consulta solo tiene acceso a la name variable de intervalo introducida por el Select operador; si el Where operador hubiera intentado hacer referencia custa , se habría producido un error en tiempo de compilación.
En lugar de especificar explícitamente los nombres de las variables de intervalo, un Select operador de consulta puede deducir los nombres de las variables de intervalo, usando las mismas reglas que las expresiones de creación de objetos de tipo anónimo. Por ejemplo:
Dim custAndOrderNames = _
From cust In Customers, ord In cust.Orders _
Select cust.name, ord.ProductName _
Where name.EndsWith("Smith")
Si no se proporciona el nombre de la variable de intervalo y no se puede deducir un nombre, se produce un error en tiempo de compilación. Si el operador de Select consulta contiene solo una expresión, no se produce ningún error si no se puede deducir un nombre para esa variable de intervalo, pero la variable de intervalo no tiene nombre. Por ejemplo:
Dim custAndOrderNames = _
From cust In Customers, ord In cust.Orders _
Select cust.Name & " bought " & ord.ProductName _
Take 10
Si hay una ambigüedad en un Select operador de consulta entre asignar un nombre a una variable de intervalo y una expresión de igualdad, se prefiere la asignación de nombres. Por ejemplo:
Dim badCustNames = _
From c In Customers _
Let name = "John Smith" _
Select name = c.Name ' Creates a range variable named "name"
Dim goodCustNames = _
From c In Customers _
Let name = "John Smith" _
Select match = (name = c.Name)
Cada expresión del Select operador de consulta debe clasificarse como un valor. Un Select operador de consulta solo se admite si el tipo de colección contiene un método:
Function Select(selector As Func(Of T, R)) As CR
El código.
Dim xs() As Integer = ...
Dim zs = From x In xs _
Select x, y = x * 10 _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim zs = _
xs.Select(Function(x As Integer) New With {x, .y = x * 10})...
Operador de consulta distinct
El Distinct operador de consulta restringe los valores de una colección solo a los que tienen valores distintos, según se determina comparando el tipo de elemento para obtener igualdad.
DistinctQueryOperator
: LineTerminator? 'Distinct' LineTerminator?
;
Por ejemplo, observe la consulta siguiente:
Dim distinctCustomerPrice = _
From cust In Customers, ord In cust.Orders _
Select cust.Name, ord.Price _
Distinct
solo devolverá una fila para cada emparejamiento distinto de nombre de cliente y precio de pedido, incluso si el cliente tiene varios pedidos con el mismo precio. Un Distinct operador de consulta solo se admite si el tipo de colección contiene un método:
Function Distinct() As CT
El código.
Dim xs() As Integer = ...
Dim zs = From x In xs _
Distinct _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim zs = xs.Distinct()...
Nota.
Distinct no es una palabra reservada.
Operador where query
El Where operador de consulta restringe los valores de una colección a los que cumplen una condición determinada.
WhereQueryOperator
: LineTerminator? 'Where' LineTerminator? BooleanExpression
;
Un Where operador de consulta toma una expresión booleana que se evalúa para cada conjunto de valores de variable de rango; si el valor de la expresión es true, los valores aparecen en la colección de salida; de lo contrario, se omiten los valores. Por ejemplo, la expresión de consulta:
From cust In Customers, ord In Orders _
Where cust.ID = ord.CustomerID _
...
se puede considerar como equivalente al bucle anidado
For Each cust In Customers
For Each ord In Orders
If cust.ID = ord.CustomerID Then
...
End If
Next ord
Next cust
Un Where operador de consulta solo se admite si el tipo de colección contiene un método:
Function Where(predicate As Func(Of T, B)) As CT
El código.
Dim xs() As Integer = ...
Dim zs = From x In xs _
Where x < 10 _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim zs = _
xs.Where(Function(x As Integer) x < 10)...
Nota.
Where no es una palabra reservada.
Operadores de consulta de particiones
PartitionQueryOperator
: LineTerminator? 'Take' LineTerminator? Expression
| LineTerminator? 'Take' 'While' LineTerminator? BooleanExpression
| LineTerminator? 'Skip' LineTerminator? Expression
| LineTerminator? 'Skip' 'While' LineTerminator? BooleanExpression
;
El Take operador de consulta da como resultado los primeros n elementos de una colección. Cuando se usa con el While modificador , el Take operador da como resultado los primeros n elementos de una colección que satisfacen una expresión booleana. El Skip operador omite los primeros n elementos de una colección y, a continuación, devuelve el resto de la colección. Cuando se usa junto con el While modificador , el Skip operador omite los primeros n elementos de una colección que satisfacen una expresión booleana y, a continuación, devuelve el resto de la colección. Las expresiones de un Take operador de consulta o Skip deben clasificarse como un valor.
Un Take operador de consulta solo se admite si el tipo de colección contiene un método:
Function Take(count As N) As CT
Un Skip operador de consulta solo se admite si el tipo de colección contiene un método:
Function Skip(count As N) As CT
Un Take While operador de consulta solo se admite si el tipo de colección contiene un método:
Function TakeWhile(predicate As Func(Of T, B)) As CT
Un Skip While operador de consulta solo se admite si el tipo de colección contiene un método:
Function SkipWhile(predicate As Func(Of T, B)) As CT
El código.
Dim xs() As Integer = ...
Dim zs = From x In xs _
Skip 10 _
Take 5 _
Skip While x < 10 _
Take While x > 5 _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim zs = _
xs.Skip(10). _
Take(5). _
SkipWhile(Function(x) x < 10). _
TakeWhile(Function(x) x > 5)...
Nota.
Take y Skip no son palabras reservadas.
Operador order by query
El Order By operador de consulta ordena los valores que aparecen en las variables de intervalo.
OrderByQueryOperator
: LineTerminator? 'Order' 'By' LineTerminator? OrderExpressionList
;
OrderExpressionList
: OrderExpression ( Comma OrderExpression )*
;
OrderExpression
: Expression Ordering?
;
Ordering
: 'Ascending' | 'Descending'
;
Un Order By operador de consulta toma expresiones que especifican los valores de clave que se deben usar para ordenar las variables de iteración. Por ejemplo, la consulta siguiente devuelve productos ordenados por precio:
Dim productsByPrice = _
From p In Products _
Order By p.Price _
Select p.Name
Una ordenación se puede marcar como Ascending, en cuyo caso los valores más pequeños vienen antes de valores más grandes, o Descending, en cuyo caso los valores más grandes vienen antes de los valores más pequeños. El valor predeterminado de una ordenación si no se especifica ninguno es Ascending. Por ejemplo, la siguiente consulta devuelve productos ordenados por precio con el producto más caro primero:
Dim productsByPriceDesc = _
From p In Products _
Order By p.Price Descending _
Select p.Name
El Order By operador de consulta puede especificar varias expresiones para ordenar, en cuyo caso la colección se ordena de forma anidada. Por ejemplo, la consulta siguiente ordena a los clientes por estado, luego por ciudad dentro de cada estado y, a continuación, por código postal dentro de cada ciudad:
Dim customersByLocation = _
From c In Customers _
Order By c.State, c.City, c.ZIP _
Select c.Name, c.State, c.City, c.ZIP
Las expresiones de un Order By operador de consulta deben clasificarse como un valor. Un Order By operador de consulta solo se admite si el tipo de colección contiene uno o ambos de los métodos siguientes:
Function OrderBy(keySelector As Func(Of T, K)) As CT
Function OrderByDescending(keySelector As Func(Of T, K)) As CT
El tipo CT de valor devuelto debe ser una colección ordenada. Una colección ordenada es un tipo de colección que contiene uno o ambos métodos:
Function ThenBy(keySelector As Func(Of T, K)) As CT
Function ThenByDescending(keySelector As Func(Of T, K)) As CT
El código.
Dim xs() As Integer = ...
Dim zs = From x In xs _
Order By x Ascending, x Mod 2 Descending _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim zs = _
xs.OrderBy(Function(x) x).ThenByDescending(Function(x) x Mod 2)...
Nota. Dado que los operadores de consulta simplemente asignan la sintaxis a los métodos que implementan una operación de consulta determinada, la conservación del orden no viene determinada por el lenguaje y viene determinada por la implementación del propio operador. Esto es muy similar a los operadores definidos por el usuario en que la implementación para sobrecargar el operador de suma para un tipo numérico definido por el usuario puede no realizar nada parecido a una adición. Por supuesto, para conservar la predictibilidad, no se recomienda implementar algo que no coincida con las expectativas del usuario.
Nota.
Order y By no son palabras reservadas.
Operador group by query
El Group By operador de consulta agrupa las variables de intervalo en el ámbito en función de una o varias expresiones y, a continuación, genera nuevas variables de rango basadas en esas agrupaciones.
GroupByQueryOperator
: LineTerminator? 'Group' ( LineTerminator? ExpressionRangeVariableDeclarationList )?
LineTerminator? 'By' LineTerminator? ExpressionRangeVariableDeclarationList
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Por ejemplo, la consulta siguiente agrupa todos los clientes por Statey, a continuación, calcula el recuento y la edad media de cada grupo:
Dim averageAges = _
From cust In Customers _
Group By cust.State _
Into Count(), Average(cust.Age)
El Group By operador de consulta tiene tres cláusulas: la cláusula opcional Group , la By cláusula y la Into cláusula . La Group cláusula tiene la misma sintaxis y efecto que un Select operador de consulta, salvo que solo afecta a las variables de intervalo disponibles en la Into cláusula y no a la By cláusula . Por ejemplo:
Dim averageAges = _
From cust In Customers _
Group cust.Age By cust.State _
Into Count(), Average(Age)
La By cláusula declara variables de intervalo de expresiones que se usan como valores clave en la operación de agrupación. La Into cláusula permite la declaración de variables de intervalo de expresiones que calculan agregaciones sobre cada uno de los grupos formados por la By cláusula . Dentro de la Into cláusula , la variable de intervalo de expresiones solo se puede asignar a una expresión que es una invocación de método de una función de agregado. Una función de agregado es una función en el tipo de colección del grupo (que puede no ser necesariamente el mismo tipo de colección de la colección original) que es similar a cualquiera de los métodos siguientes:
Function _name_() As _type_
Function _name_(selector As Func(Of T, R)) As R
Si una función de agregado toma un argumento delegado, la expresión de invocación puede tener una expresión de argumento que se debe clasificar como un valor. La expresión de argumento puede usar las variables de intervalo que están en el ámbito; dentro de la llamada a una función de agregado, esas variables de intervalo representan los valores del grupo que se está formando, no todos los valores de la colección. Por ejemplo, en el ejemplo original de esta sección, la Average función calcula el promedio de edades de los clientes por estado en lugar de para todos los clientes juntos.
Todos los tipos de colección se consideran que tienen definida la función Group de agregado en ella, que no toma parámetros y simplemente devuelve el grupo. Otras funciones de agregado estándar que puede proporcionar un tipo de colección son:
Count y LongCount, que devuelven el recuento de los elementos del grupo o el recuento de los elementos del grupo que satisfacen una expresión booleana.
Count y LongCount solo se admiten si el tipo de colección contiene uno de los métodos:
Function Count() As N
Function Count(selector As Func(Of T, B)) As N
Function LongCount() As N
Function LongCount(selector As Func(Of T, B)) As N
Sum, que devuelve la suma de una expresión en todos los elementos del grupo.
Sum solo se admite si el tipo de colección contiene uno de los métodos:
Function Sum() As N
Function Sum(selector As Func(Of T, N)) As N
Min que devuelve el valor mínimo de una expresión en todos los elementos del grupo.
Min solo se admite si el tipo de colección contiene uno de los métodos:
Function Min() As N
Function Min(selector As Func(Of T, N)) As N
Max, que devuelve el valor máximo de una expresión en todos los elementos del grupo.
Max solo se admite si el tipo de colección contiene uno de los métodos:
Function Max() As N
Function Max(selector As Func(Of T, N)) As N
Average, que devuelve el promedio de una expresión en todos los elementos del grupo.
Average solo se admite si el tipo de colección contiene uno de los métodos:
Function Average() As N
Function Average(selector As Func(Of T, N)) As N
Any, que determina si un grupo contiene miembros o si una expresión booleana es true para cualquier elemento del grupo.
Any devuelve un valor que se puede usar en una expresión booleana y solo se admite si el tipo de colección contiene uno de los métodos:
Function Any() As B
Function Any(predicate As Func(Of T, B)) As B
All, que determina si una expresión booleana es true para todos los elementos del grupo.
All devuelve un valor que se puede usar en una expresión booleana y solo se admite si el tipo de colección contiene un método:
Function All(predicate As Func(Of T, B)) As B
Después de un Group By operador de consulta, las variables de intervalo anteriormente en el ámbito están ocultas y las variables de intervalo introducidas por las By cláusulas y Into están disponibles. Un Group By operador de consulta solo se admite si el tipo de colección contiene el método :
Function GroupBy(keySelector As Func(Of T, K), _
resultSelector As Func(Of K, CT, R)) As CR
Las declaraciones de variables de intervalo en la Group cláusula solo se admiten si el tipo de colección contiene el método :
Function GroupBy(keySelector As Func(Of T, K), _
elementSelector As Func(Of T, S), _
resultSelector As Func(Of K, CS, R)) As CR
El código.
Dim xs() As Integer = ...
Dim zs = From x In xs _
Group y = x * 10, z = x / 10 By evenOdd = x Mod 2 _
Into Sum(y), Average(z) _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim zs = _
xs.GroupBy( _
Function(x As Integer) x Mod 2, _
Function(x As Integer) New With {.y = x * 10, .z = x / 10}, _
Function(evenOdd, group) New With { _
evenOdd, _
.Sum = group.Sum(Function(e) e.y), _
.Average = group.Average(Function(e) e.z)})...
Note.Group, By, y Into no son palabras reservadas.
Operador de consulta de agregado
El Aggregate operador de consulta realiza una función similar al Group By operador , salvo que permite agregar sobre grupos que ya se han formado. Dado que el grupo ya se ha formado, la Into cláusula de un Aggregate operador de consulta no oculta las variables de intervalo en el ámbito (de esta manera, Aggregate es más similar a , Lety Group By es más similar a ).Select
AggregateQueryOperator
: LineTerminator? 'Aggregate' LineTerminator? CollectionRangeVariableDeclaration QueryOperator*
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Por ejemplo, la consulta siguiente agrega el total de todos los pedidos realizados por los clientes en Washington:
Dim orderTotals = _
From cust In Customers _
Where cust.State = "WA" _
Aggregate order In cust.Orders _
Into Sum(order.Total)
El resultado de esta consulta es una colección cuyo tipo de elemento es un tipo anónimo con una propiedad denominada cust typed como Customer y una propiedad denominada typed Sum como Integer.
A diferencia Group Byde , los operadores de consulta adicionales se pueden colocar entre las Aggregate cláusulas y Into . Entre una Aggregate cláusula y el final de la Into cláusula , se pueden usar todas las variables de intervalo en el ámbito, incluidas las declaradas por la Aggregate cláusula . Por ejemplo, la siguiente consulta agrega el total total de todos los pedidos realizados por los clientes de Washington antes de 2006:
Dim orderTotals = _
From cust In Customers _
Where cust.State = "WA" _
Aggregate order In cust.Orders _
Where order.Date <= #01/01/2006# _
Into Sum = Sum(order.Total)
El Aggregate operador también se puede usar para iniciar una expresión de consulta. En este caso, el resultado de la expresión de consulta será el valor único calculado por la Into cláusula . Por ejemplo, la consulta siguiente calcula la suma de todos los totales de pedidos antes del 1 de enero de 2006:
Dim ordersTotal = _
Aggregate order In Orders _
Where order.Date <= #01/01/2006# _
Into Sum(order.Total)
El resultado de la consulta es un valor único Integer . Un Aggregate operador de consulta siempre está disponible (aunque la función de agregado también debe estar disponible para que la expresión sea válida). El código.
Dim xs() As Integer = ...
Dim zs = _
Aggregate x In xs _
Where x < 5 _
Into Sum()
normalmente se traduce a
Dim xs() As Integer = ...
Dim zs = _
xs.Where(Function(x) x < 5).Sum()
Nota.
Aggregate y Into no son palabras reservadas.
Operador de consulta de combinación de grupo
El Group Join operador de consulta combina las funciones de los Join operadores de consulta y Group By en un único operador.
Group Join combina dos colecciones basadas en claves coincidentes extraídas de los elementos, agrupando todos los elementos del lado derecho de la combinación que coinciden con un elemento determinado en el lado izquierdo de la combinación. Por lo tanto, el operador genera un conjunto de resultados jerárquicos.
GroupJoinQueryOperator
: LineTerminator? 'Group' 'Join' LineTerminator? CollectionRangeVariableDeclaration
JoinOrGroupJoinQueryOperator? LineTerminator? 'On' LineTerminator? JoinConditionList
LineTerminator? 'Into' LineTerminator? ExpressionRangeVariableDeclarationList
;
Por ejemplo, la consulta siguiente genera elementos que contienen el nombre de un solo cliente, un grupo de todos sus pedidos y la cantidad total de todos esos pedidos:
Dim custsWithOrders = _
From cust In Customers _
Group Join order In Orders On cust.ID Equals order.CustomerID _
Into Orders = Group, OrdersTotal = Sum(order.Total) _
Select cust.Name, Orders, OrdersTotal
El resultado de la consulta es una colección cuyo tipo de elemento es un tipo anónimo con tres propiedades: Name, se escribe como String, Orders se escribe como una colección cuyo tipo de elemento es Order, y OrdersTotal, que se escribe como Integer. Un Group Join operador de consulta solo se admite si el tipo de colección contiene el método :
Function GroupJoin(inner As CS, _
outerSelector As Func(Of T, K), _
innerSelector As Func(Of S, K), _
resultSelector As Func(Of T, CS, R)) As CR
El código.
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = From x In xs _
Group Join y in ys On x Equals y _
Into g = Group _
...
normalmente se traduce a
Dim xs() As Integer = ...
Dim ys() As Integer = ...
Dim zs = _
xs.GroupJoin( _
ys, _
Function(x As Integer) x, _
Function(y As Integer) y, _
Function(x, group) New With {x, .g = group})...
Note.Group, Join, y Into no son palabras reservadas.
Expresiones condicionales
Una expresión condicional If prueba una expresión y devuelve un valor.
ConditionalExpression
: 'If' OpenParenthesis BooleanExpression Comma Expression Comma Expression CloseParenthesis
| 'If' OpenParenthesis Expression Comma Expression CloseParenthesis
;
A diferencia de la IIF función en tiempo de ejecución, sin embargo, una expresión condicional solo evalúa sus operandos si es necesario. Por lo tanto, por ejemplo, la expresión If(c Is Nothing, c.Name, "Unknown") no producirá una excepción si el valor de c es Nothing. La expresión condicional tiene dos formas: una que toma dos operandos y otro que toma tres operandos.
Si se proporcionan tres operandos, las tres expresiones deben clasificarse como valores y el primer operando debe ser una expresión booleana. Si el resultado es de la expresión es true, la segunda expresión será el resultado del operador; de lo contrario, la tercera expresión será el resultado del operador. El tipo de resultado de la expresión es el tipo dominante entre los tipos de la segunda y la tercera expresión. Si no hay ningún tipo dominante, se produce un error en tiempo de compilación.
Si se proporcionan dos operandos, ambos operandos deben clasificarse como valores y el primer operando debe ser un tipo de referencia o un tipo de valor que acepta valores NULL. A continuación, la expresión If(x, y) se evalúa como si fuera la expresión If(x IsNot Nothing, x, y), con dos excepciones. En primer lugar, la primera expresión solo se evalúa una vez y, en segundo lugar, si el segundo tipo de operando es un tipo de valor que no acepta valores NULL y el tipo del primer operando es , ? se quita del tipo del primer operando al determinar el tipo dominante para el tipo de resultado de la expresión. Por ejemplo:
Module Test
Sub Main()
Dim x?, y As Integer
Dim a?, b As Long
a = If(x, a) ' Result type: Long?
y = If(x, 0) ' Result type: Integer
End Sub
End Module
En ambas formas de la expresión, si un operando es Nothing, su tipo no se usa para determinar el tipo dominante. En el caso de la expresión If(<expression>, Nothing, Nothing), el tipo dominante se considera Object.
Expresiones literales XML
Una expresión literal XML representa un valor xml (lenguaje de marcado eXtensible) 1.0.
XMLLiteralExpression
: XMLDocument
| XMLElement
| XMLProcessingInstruction
| XMLComment
| XMLCDATASection
;
El resultado de una expresión literal XML es un valor escrito como uno de los tipos del System.Xml.Linq espacio de nombres. Si los tipos de ese espacio de nombres no están disponibles, una expresión literal XML provocará un error en tiempo de compilación. Los valores se generan a través de llamadas de constructor traducidas a partir de la expresión literal XML. Por ejemplo, el código:
Dim book As System.Xml.Linq.XElement = _
<book title="My book"></book>
es aproximadamente equivalente al código:
Dim book As System.Xml.Linq.XElement = _
New System.Xml.Linq.XElement( _
"book", _
New System.Xml.Linq.XAttribute("title", "My book"))
Una expresión literal XML puede adoptar la forma de un documento XML, un elemento XML, una instrucción de procesamiento XML, un comentario XML o una sección CDATA.
Nota. Esta especificación contiene solo suficiente una descripción de XML para describir el comportamiento del lenguaje Visual Basic. Puede encontrar más información sobre XML en http://www.w3.org/TR/REC-xml/.
Reglas léxicas
XMLCharacter
: '<Unicode tab character (0x0009)>'
| '<Unicode linefeed character (0x000A)>'
| '<Unicode carriage return character (0x000D)>'
| '<Unicode characters 0x0020 - 0xD7FF>'
| '<Unicode characters 0xE000 - 0xFFFD>'
| '<Unicode characters 0x10000 - 0x10FFFF>'
;
XMLString
: XMLCharacter+
;
XMLWhitespace
: XMLWhitespaceCharacter+
;
XMLWhitespaceCharacter
: '<Unicode carriage return character (0x000D)>'
| '<Unicode linefeed character (0x000A)>'
| '<Unicode space character (0x0020)>'
| '<Unicode tab character (0x0009)>'
;
XMLNameCharacter
: XMLLetter
| XMLDigit
| '.'
| '-'
| '_'
| ':'
| XMLCombiningCharacter
| XMLExtender
;
XMLNameStartCharacter
: XMLLetter
| '_'
| ':'
;
XMLName
: XMLNameStartCharacter XMLNameCharacter*
;
XMLLetter
: '<Unicode character as defined in the Letter production of the XML 1.0 specification>'
;
XMLDigit
: '<Unicode character as defined in the Digit production of the XML 1.0 specification>'
;
XMLCombiningCharacter
: '<Unicode character as defined in the CombiningChar production of the XML 1.0 specification>'
;
XMLExtender
: '<Unicode character as defined in the Extender production of the XML 1.0 specification>'
;
Las expresiones literales XML se interpretan mediante las reglas léxicas de XML en lugar de las reglas léxicas del código normal de Visual Basic. Los dos conjuntos de reglas suelen diferir de las siguientes maneras:
El espacio en blanco es significativo en XML. Como resultado, la gramática de las expresiones literales XML indica explícitamente dónde se permite el espacio en blanco. El espacio en blanco no se conserva, excepto cuando se produce en el contexto de los datos de caracteres dentro de un elemento. Por ejemplo:
' The following element preserves no whitespace Dim e1 = _ <customer> <name>Bob</> </> ' The following element preserves all of the whitespace Dim e2 = _ <customer> Bob </>El espacio en blanco de fin de línea XML se normaliza según la especificación XML.
XML distingue mayúsculas de minúsculas. Las palabras clave deben coincidir exactamente con mayúsculas o minúsculas, o bien se producirá un error en tiempo de compilación.
Los terminadores de línea se consideran espacios en blanco en XML. Como resultado, no se necesitan caracteres de continuación de línea en expresiones literales XML.
XML no acepta caracteres de ancho completo. Si se usan caracteres de ancho completo, se producirá un error en tiempo de compilación.
Expresiones incrustadas
Las expresiones literales XML pueden contener expresiones incrustadas. Una expresión incrustada es una expresión de Visual Basic que se evalúa y se usa para rellenar uno o varios valores en la ubicación de la expresión incrustada.
XMLEmbeddedExpression
: '<' '%' '=' LineTerminator? Expression LineTerminator? '%' '>'
;
Por ejemplo, el código siguiente coloca la cadena John Smith como el valor del elemento XML:
Dim name as String = "John Smith"
Dim element As System.Xml.Linq.XElement = <customer><%= name %></customer>
Las expresiones se pueden incrustar en varios contextos. Por ejemplo, el código siguiente genera un elemento denominado customer:
Dim name As String = "customer"
Dim element As System.Xml.Linq.XElement = <<%= name %>>John Smith</>
Cada contexto en el que se puede usar una expresión incrustada especifica los tipos que se aceptarán. Cuando dentro del contexto de la parte de expresión de una expresión incrustada, las reglas léxicas normales para el código de Visual Basic se siguen aplicando, por ejemplo, se deben usar continuaciones de línea:
' Visual Basic expression uses line continuation, XML does not
Dim element As System.Xml.Linq.XElement = _
<<%= name & _
name %>>John
Smith</>
Documentos XML
XMLDocument
: XMLDocumentPrologue XMLMisc* XMLDocumentBody XMLMisc*
;
XMLDocumentPrologue
: '<' '?' 'xml' XMLVersion XMLEncoding? XMLStandalone? XMLWhitespace? '?' '>'
;
XMLVersion
: XMLWhitespace 'version' XMLWhitespace? '=' XMLWhitespace? XMLVersionNumberValue
;
XMLVersionNumberValue
: SingleQuoteCharacter '1' '.' '0' SingleQuoteCharacter
| DoubleQuoteCharacter '1' '.' '0' DoubleQuoteCharacter
;
XMLEncoding
: XMLWhitespace 'encoding' XMLWhitespace? '=' XMLWhitespace? XMLEncodingNameValue
;
XMLEncodingNameValue
: SingleQuoteCharacter XMLEncodingName SingleQuoteCharacter
| DoubleQuoteCharacter XMLEncodingName DoubleQuoteCharacter
;
XMLEncodingName
: XMLLatinAlphaCharacter XMLEncodingNameCharacter*
;
XMLEncodingNameCharacter
: XMLUnderscoreCharacter
| XMLLatinAlphaCharacter
| XMLNumericCharacter
| XMLPeriodCharacter
| XMLDashCharacter
;
XMLLatinAlphaCharacter
: '<Unicode Latin alphabetic character (0x0041-0x005a, 0x0061-0x007a)>'
;
XMLNumericCharacter
: '<Unicode digit character (0x0030-0x0039)>'
;
XMLHexNumericCharacter
: XMLNumericCharacter
| '<Unicode Latin hex alphabetic character (0x0041-0x0046, 0x0061-0x0066)>'
;
XMLPeriodCharacter
: '<Unicode period character (0x002e)>'
;
XMLUnderscoreCharacter
: '<Unicode underscore character (0x005f)>'
;
XMLDashCharacter
: '<Unicode dash character (0x002d)>'
;
XMLStandalone
: XMLWhitespace 'standalone' XMLWhitespace? '=' XMLWhitespace? XMLYesNoValue
;
XMLYesNoValue
: SingleQuoteCharacter XMLYesNo SingleQuoteCharacter
| DoubleQuoteCharacter XMLYesNo DoubleQuoteCharacter
;
XMLYesNo
: 'yes'
| 'no'
;
XMLMisc
: XMLComment
| XMLProcessingInstruction
| XMLWhitespace
;
XMLDocumentBody
: XMLElement
| XMLEmbeddedExpression
;
Un documento XML da como resultado un valor escrito como System.Xml.Linq.XDocument. A diferencia de la especificación XML 1.0, se requieren documentos XML en expresiones literales XML para especificar el prólogo del documento XML; Las expresiones literales XML sin el prólogo del documento XML se interpretan como su entidad individual. Por ejemplo:
Dim doc As System.Xml.Linq.XDocument = _
<?xml version="1.0"?>
<?instruction?>
<customer>Bob</>
Dim pi As System.Xml.Linq.XProcessingInstruction = _
<?instruction?>
Un documento XML puede contener una expresión incrustada cuyo tipo puede ser cualquier tipo; Sin embargo, en tiempo de ejecución, el objeto debe cumplir los requisitos del XDocument constructor o se producirá un error en tiempo de ejecución.
A diferencia de XML normal, las expresiones de documento XML no admiten DTD (declaraciones de tipo de documento). Además, el atributo de codificación, si se proporciona, se omitirá, ya que la codificación de la expresión literal Xml siempre es la misma que la codificación del propio archivo de origen.
Nota. Aunque se omite el atributo de codificación, sigue siendo un atributo válido para mantener la capacidad de incluir cualquier documento Xml 1.0 válido en el código fuente.
Elementos XML
XMLElement
: XMLEmptyElement
| XMLElementStart XMLContent XMLElementEnd
;
XMLEmptyElement
: '<' XMLQualifiedNameOrExpression XMLAttribute* XMLWhitespace? '/' '>'
;
XMLElementStart
: '<' XMLQualifiedNameOrExpression XMLAttribute* XMLWhitespace? '>'
;
XMLElementEnd
: '<' '/' '>'
| '<' '/' XMLQualifiedName XMLWhitespace? '>'
;
XMLContent
: XMLCharacterData? ( XMLNestedContent XMLCharacterData? )+
;
XMLCharacterData
: '<Any XMLCharacterDataString that does not contain the string "]]>">'
;
XMLCharacterDataString
: '<Any Unicode character except < or &>'+
;
XMLNestedContent
: XMLElement
| XMLReference
| XMLCDATASection
| XMLProcessingInstruction
| XMLComment
| XMLEmbeddedExpression
;
XMLAttribute
: XMLWhitespace XMLAttributeName XMLWhitespace? '=' XMLWhitespace? XMLAttributeValue
| XMLWhitespace XMLEmbeddedExpression
;
XMLAttributeName
: XMLQualifiedNameOrExpression
| XMLNamespaceAttributeName
;
XMLAttributeValue
: DoubleQuoteCharacter XMLAttributeDoubleQuoteValueCharacter* DoubleQuoteCharacter
| SingleQuoteCharacter XMLAttributeSingleQuoteValueCharacter* SingleQuoteCharacter
| XMLEmbeddedExpression
;
XMLAttributeDoubleQuoteValueCharacter
: '<Any XMLCharacter except <, &, or DoubleQuoteCharacter>'
| XMLReference
;
XMLAttributeSingleQuoteValueCharacter
: '<Any XMLCharacter except <, &, or SingleQuoteCharacter>'
| XMLReference
;
XMLReference
: XMLEntityReference
| XMLCharacterReference
;
XMLEntityReference
: '&' XMLEntityName ';'
;
XMLEntityName
: 'lt' | 'gt' | 'amp' | 'apos' | 'quot'
;
XMLCharacterReference
: '&' '#' XMLNumericCharacter+ ';'
| '&' '#' 'x' XMLHexNumericCharacter+ ';'
;
Un elemento XML da como resultado un valor escrito como System.Xml.Linq.XElement. A diferencia del XML normal, los elementos XML pueden omitir el nombre en la etiqueta de cierre y se cerrará el elemento más anidado actual. Por ejemplo:
Dim name = <name>Bob</>
Las declaraciones de atributo de un elemento XML dan como resultado valores tipados como System.Xml.Linq.XAttribute. Los valores de atributo se normalizan según la especificación XML. Cuando el valor de un atributo es Nothing el atributo no se creará, por lo que la expresión de valor de atributo no tendrá que comprobarse para Nothing. Por ejemplo:
Dim expr = Nothing
' Throws null argument exception
Dim direct = New System.Xml.Linq.XElement( _
"Name", _
New System.Xml.Linq.XAttribute("Length", expr))
' Doesn't throw exception, the result is <Name/>
Dim literal = <Name Length=<%= expr %>/>
Los elementos y atributos XML pueden contener expresiones anidadas en los siguientes lugares:
Nombre del elemento, en cuyo caso la expresión incrustada debe ser un valor de un tipo convertible implícitamente a System.Xml.Linq.XName. Por ejemplo:
Dim name = <<%= "name" %>>Bob</>
Nombre de un atributo del elemento, en cuyo caso la expresión incrustada debe ser un valor de un tipo convertible implícitamente a System.Xml.Linq.XName. Por ejemplo:
Dim name = <name <%= "length" %>="3">Bob</>
Valor de un atributo del elemento, en cuyo caso la expresión incrustada puede ser un valor de cualquier tipo. Por ejemplo:
Dim name = <name length=<%= 3 %>>Bob</>
Atributo del elemento, en cuyo caso la expresión incrustada puede ser un valor de cualquier tipo. Por ejemplo:
Dim name = <name <%= new XAttribute("length", 3) %>>Bob</>
El contenido del elemento, en cuyo caso la expresión incrustada puede ser un valor de cualquier tipo. Por ejemplo:
Dim name = <name><%= "Bob" %></>
Si el tipo de la expresión incrustada es Object(), la matriz se pasará como paramarray al XElement constructor.
Espacios de nombres XML
Los elementos XML pueden contener declaraciones de espacio de nombres XML, tal como se define en la especificación de espacios de nombres XML 1.0.
XMLNamespaceAttributeName
: XMLPrefixedNamespaceAttributeName
| XMLDefaultNamespaceAttributeName
;
XMLPrefixedNamespaceAttributeName
: 'xmlns' ':' XMLNamespaceName
;
XMLDefaultNamespaceAttributeName
: 'xmlns'
;
XMLNamespaceName
: XMLNamespaceNameStartCharacter XMLNamespaceNameCharacter*
;
XMLNamespaceNameStartCharacter
: '<Any XMLNameCharacter except :>'
;
XMLNamespaceNameCharacter
: XMLLetter
| '_'
;
XMLQualifiedNameOrExpression
: XMLQualifiedName
| XMLEmbeddedExpression
;
XMLQualifiedName
: XMLPrefixedName
| XMLUnprefixedName
;
XMLPrefixedName
: XMLNamespaceName ':' XMLNamespaceName
;
XMLUnprefixedName
: XMLNamespaceName
;
Las restricciones para definir los espacios de nombres xml y xmlns se aplican y producirán errores en tiempo de compilación. Las declaraciones de espacio de nombres XML no pueden tener una expresión incrustada para su valor; el valor proporcionado debe ser un literal de cadena no vacío. Por ejemplo:
' Declares a valid namespace
Dim customer = <db:customer xmlns:db="http://example.org/database">Bob</>
' Error: xmlns cannot be re-defined
Dim bad1 = <elem xmlns:xmlns="http://example.org/namespace"/>
' Error: cannot have an embedded expression
Dim bad2 = <elem xmlns:db=<%= "http://example.org/database" %>>Bob</>
Nota. Esta especificación solo contiene suficiente una descripción del espacio de nombres XML para describir el comportamiento del lenguaje Visual Basic. Puede encontrar más información sobre los espacios de nombres XML en http://www.w3.org/TR/REC-xml-names/.
Los nombres de atributo y elemento XML se pueden calificar mediante nombres de espacio de nombres. Los espacios de nombres se enlazan como en XML normal, con la excepción de que las importaciones de espacio de nombres declaradas en el nivel de archivo se consideran declaradas en un contexto que incluye la declaración, que se incluye entre las importaciones de espacios de nombres declaradas por el entorno de compilación. Si no se encuentra un nombre de espacio de nombres, se produce un error en tiempo de compilación. Por ejemplo:
Imports System.Xml.Linq
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
' Binds to the imported namespace above.
Dim c1 = <db:customer>Bob</>
' Binds to the namespace declaration in the element
Dim c2 = _
<db:customer xmlns:db="http://example.org/database-other">Mary</>
' Binds to the inner namespace declaration
Dim c3 = _
<database xmlns:db="http://example.org/database-one">
<db:customer xmlns:db="http://example.org/database-two">Joe</>
</>
' Error: namespace db2 cannot be found
Dim c4 = _
<db2:customer>Jim</>
End Sub
End Module
Los espacios de nombres XML declarados en un elemento no se aplican a los literales XML dentro de expresiones incrustadas. Por ejemplo:
' Error: Namespace prefix 'db' is not declared
Dim customer = _
<db:customer xmlns:db="http://example.org/database">
<%= <db:customer>Bob</> %>
</>
Nota. Esto se debe a que la expresión incrustada puede ser cualquier cosa, incluida una llamada de función. Si la llamada de función contenía una expresión literal XML, no está claro si los programadores esperarían que se aplique o se omita el espacio de nombres XML.
Instrucciones de procesamiento XML
Una instrucción de procesamiento XML da como resultado un valor escrito como System.Xml.Linq.XProcessingInstruction. Las instrucciones de procesamiento XML no pueden contener expresiones incrustadas, ya que son sintaxis válidas dentro de la instrucción de procesamiento.
XMLProcessingInstruction
: '<' '?' XMLProcessingTarget ( XMLWhitespace XMLProcessingValue? )? '?' '>'
;
XMLProcessingTarget
: '<Any XMLName except a casing permutation of the string "xml">'
;
XMLProcessingValue
: '<Any XMLString that does not contain a question-mark followed by ">">'
;
Comentarios XML
Un comentario XML da como resultado un valor escrito como System.Xml.Linq.XComment. Los comentarios XML no pueden contener expresiones incrustadas, ya que son sintaxis válidas dentro del comentario.
XMLComment
: '<' '!' '-' '-' XMLCommentCharacter* '-' '-' '>'
;
XMLCommentCharacter
: '<Any XMLCharacter except dash (0x002D)>'
| '-' '<Any XMLCharacter except dash (0x002D)>'
;
Secciones de CDATA
Una sección CDATA da como resultado un valor escrito como System.Xml.Linq.XCData. Las secciones de CDATA no pueden contener expresiones incrustadas, ya que son sintaxis válidas dentro de la sección CDATA.
XMLCDATASection
: '<' '!' ( 'CDATA' '[' XMLCDATASectionString? ']' )? '>'
;
XMLCDATASectionString
: '<Any XMLString that does not contain the string "]]>">'
;
Expresiones de acceso a miembros XML
Una expresión de acceso de miembro XML accede a los miembros de un valor XML.
XMLMemberAccessExpression
: Expression '.' LineTerminator? '<' XMLQualifiedName '>'
| Expression '.' LineTerminator? '@' LineTerminator? '<' XMLQualifiedName '>'
| Expression '.' LineTerminator? '@' LineTerminator? IdentifierOrKeyword
| Expression '.' '.' '.' LineTerminator? '<' XMLQualifiedName '>'
;
Hay tres tipos de expresiones de acceso a miembros XML:
Acceso a elementos, en el que un nombre XML sigue un único punto. Por ejemplo:
Dim customer = _ <customer> <name>Bob</> </> Dim customerName = customer.<name>.ValueEl acceso a elementos se asigna a la función :
Function Elements(name As System.Xml.Linq.XName) As _ System.Collections.Generic.IEnumerable(Of _ System.Xml.Linq.XNode)Por lo tanto, el ejemplo anterior es equivalente a:
Dim customerName = customer.Elements("name").ValueAcceso a atributos, en el que un identificador de Visual Basic sigue un punto y un signo, o un nombre XML sigue un punto y un signo. Por ejemplo:
Dim customer = <customer age="30"/> Dim customerAge = customer.@ageEl acceso a atributos se asigna a la función :
Function AttributeValue(name As System.Xml.Linq.XName) as StringPor lo tanto, el ejemplo anterior es equivalente a:
Dim customerAge = customer.AttributeValue("age")Nota. El
AttributeValuemétodo de extensión (así como la propiedadValuede extensión relacionada ) no está definido actualmente en ningún ensamblado. Si se necesitan los miembros de la extensión, se definen automáticamente en el ensamblado que se está produciendo.Desciende el acceso, en el que los nombres XML siguen tres puntos. Por ejemplo:
Dim company = _ <company> <customers> <customer>Bob</> <customer>Mary</> <customer>Joe</> </> </> Dim customers = company...<customer>Descendientes de acceso se asignan a la función:
Function Descendents(name As System.Xml.Linq.XName) As _ System.Collections.Generic.IEnumerable(Of _ System.Xml.Linq.XElement)Por lo tanto, el ejemplo anterior es equivalente a:
Dim customers = company.Descendants("customer")
La expresión base de una expresión de acceso de miembro XML debe ser un valor y debe ser del tipo :
Si un elemento o descendiente tiene acceso,
System.Xml.Linq.XContainero un tipo derivado, oSystem.Collections.Generic.IEnumerable(Of T)un tipo derivado, dondeTesSystem.Xml.Linq.XContainero un tipo derivado.Si un atributo tiene acceso,
System.Xml.Linq.XElemento un tipo derivado, oSystem.Collections.Generic.IEnumerable(Of T)un tipo derivado, dondeTesSystem.Xml.Linq.XElemento un tipo derivado.
Los nombres de las expresiones de acceso a miembros XML no pueden estar vacíos. Pueden ser espacios de nombres calificados, con los espacios de nombres definidos por las importaciones. Por ejemplo:
Imports <xmlns:db="http://example.org/database">
Module Test
Sub Main()
Dim customer = _
<db:customer>
<db:name>Bob</>
</>
Dim name = customer.<db:name>
End Sub
End Module
No se permite el espacio en blanco después de los puntos de una expresión de acceso de miembro XML, o entre los corchetes angulares y el nombre. Por ejemplo:
Dim customer = _
<customer age="30">
<name>Bob</>
</>
' All the following are error cases
Dim age = customer.@ age
Dim name = customer.< name >
Dim names = customer...< name >
Si los tipos del System.Xml.Linq espacio de nombres no están disponibles, una expresión de acceso a miembros XML provocará un error en tiempo de compilación.
Operador Await
El operador await está relacionado con los métodos asincrónicos, que se describen en Métodos asincrónicos de sección.
AwaitOperatorExpression
: 'Await' Expression
;
Await es una palabra reservada si el método envolvente inmediatamente o la expresión lambda en la que aparece tiene un Async modificador y, si Await aparece después de ese Async modificador, no se proporciona en otro lugar. También no se proporciona en directivas de preprocesador. El operador await solo se permite en el cuerpo de un método o expresiones lambda donde es una palabra reservada. Dentro del método o lambda envolvente inmediatamente, es posible que una expresión await no se produzca dentro del cuerpo de un Catch bloque o Finally , ni dentro del cuerpo de una SyncLock instrucción , ni dentro de una expresión de consulta.
El operador await toma una expresión única que debe clasificarse como un valor y cuyo tipo debe ser un tipo que se puede esperar o Object. Si su tipo es Object entonces todo el procesamiento se aplaza hasta el tiempo de ejecución. Se dice que un tipo C es awaitable si se cumplen todas las siguientes condiciones:
Ccontiene una instancia o método de extensión accesible denominadoGetAwaiterque no tiene argumentos y que devuelve algún tipoE;Econtiene una propiedad de extensión o instancia legible denominadaIsCompletedque no toma argumentos y tiene el tipo Boolean;Econtiene una instancia o método de extensión accesible denominadoGetResultque no toma argumentos;ESystem.Runtime.CompilerServices.INotifyCompletionimplementa oICriticalNotifyCompletion.
Si GetResult era , Subla expresión await se clasifica como void. De lo contrario, la expresión await se clasifica como un valor y su tipo es el tipo de valor devuelto del GetResult método .
Este es un ejemplo de una clase que se puede esperar:
Class MyTask(Of T)
Function GetAwaiter() As MyTaskAwaiter(Of T)
Return New MyTaskAwaiter With {.m_Task = Me}
End Function
...
End Class
Structure MyTaskAwaiter(Of T)
Implements INotifyCompletion
Friend m_Task As MyTask(Of T)
ReadOnly Property IsCompleted As Boolean
Get
Return m_Task.IsCompleted
End Get
End Property
Sub OnCompleted(r As Action) Implements INotifyCompletion.OnCompleted
' r is the "resumptionDelegate"
Dim sc = SynchronizationContext.Current
If sc Is Nothing Then
m_Task.ContinueWith(Sub() r())
Else
m_Task.ContinueWith(Sub() sc.Post(Sub() r(), Nothing))
End If
End Sub
Function GetResult() As T
If m_Task.IsCanceled Then Throw New TaskCanceledException(m_Task)
If m_Task.IsFaulted Then Throw m_Task.Exception.InnerException
Return m_Task.Result
End Function
End Structure
Nota. Se recomienda a los autores de bibliotecas seguir el patrón en el que invocan al delegado de continuación en el mismo SynchronizationContext que se invocó a sí OnCompleted mismo. Además, el delegado de reanudación no se debe ejecutar de forma sincrónica en el OnCompleted método, ya que esto puede provocar desbordamiento de pila: en su lugar, el delegado se debe poner en cola para la ejecución posterior.
Cuando el flujo de control alcanza un Await operador, el comportamiento es el siguiente.
Se
GetAwaiterinvoca el método del operando await. El resultado de esta invocación se denomina awaiter.Se recupera la propiedad del
IsCompletedawaiter. Si el resultado es true, haga lo siguiente:- Se
GetResultinvoca el método del awaiter. SiGetResultera una función, el valor de la expresión await es el valor devuelto de esta función.
- Se
Si la propiedad IsCompleted no es true, entonces:
Se
ICriticalNotifyCompletion.UnsafeOnCompletedinvoca en el awaiter (si el tipoEdel awaitr implementaICriticalNotifyCompletion) oINotifyCompletion.OnCompleted(de lo contrario). En ambos casos, pasa un delegado de reanudación asociado a la instancia actual del método asincrónico.El punto de control de la instancia de método asincrónico actual se suspende y el flujo de control se reanuda en el llamador actual (definido en Section Async Methods).
Si más adelante se invoca el delegado de reanudación,
- el delegado de reanudación restaura
System.Threading.Thread.CurrentThread.ExecutionContextprimero a lo que estaba en el momentoOnCompleteden que se llamó, - a continuación, reanuda el flujo de control en el punto de control de la instancia del método asincrónico (vea Section Async Methods),
- donde llama al
GetResultmétodo del awaiter, como en la versión 2.1 anterior.
- el delegado de reanudación restaura
Si el operando await tiene el tipo Object, este comportamiento se aplaza hasta el tiempo de ejecución:
- El paso 1 se logra llamando a GetAwaiter() sin argumentos; por lo tanto, puede enlazarse en tiempo de ejecución a métodos de instancia que toman parámetros opcionales.
- El paso 2 se logra recuperando la propiedad IsCompleted() sin argumentos y intentando realizar una conversión intrínseca a Boolean.
- El paso 3.a se logra mediante el intento
TryCast(awaiter, ICriticalNotifyCompletion)de y, si se produce un error,DirectCast(awaiter, INotifyCompletion).
El delegado de reanudación pasado en 3.a solo se puede invocar una vez. Si se invoca más de una vez, el comportamiento no está definido.
Visual Basic language spec