Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
As duas categorias fundamentais de tipos no Visual Basic são tipos de valor e tipos de referência. Tipos primitivos (exceto cadeias de caracteres), enumerações e estruturas são tipos de valor. Classes, cadeias de caracteres, módulos padrão, interfaces, matrizes e delegados são tipos de referência.
Cada tipo tem um valor padrão, que é o valor atribuído a variáveis desse tipo após a inicialização.
TypeName
: ArrayTypeName
| NonArrayTypeName
;
NonArrayTypeName
: SimpleTypeName
| NullableTypeName
;
SimpleTypeName
: QualifiedTypeName
| BuiltInTypeName
;
QualifiedTypeName
: Identifier TypeArguments? (Period IdentifierOrKeyword TypeArguments?)*
| 'Global' Period IdentifierOrKeyword TypeArguments?
(Period IdentifierOrKeyword TypeArguments?)*
;
TypeArguments
: OpenParenthesis 'Of' TypeArgumentList CloseParenthesis
;
TypeArgumentList
: TypeName ( Comma TypeName )*
;
BuiltInTypeName
: 'Object'
| PrimitiveTypeName
;
TypeModifier
: AccessModifier
| 'Shadows'
;
IdentifierModifiers
: NullableNameModifier? ArrayNameModifier?
;
Tipos de valor e tipos de referência
Embora tipos de valor e tipos de referência possam ser semelhantes em termos de sintaxe de declaração e uso, sua semântica é distinta.
Os tipos de referência são armazenados no heap em tempo de execução; eles só podem ser acessados por meio de uma referência a esse armazenamento. Como os tipos de referência são sempre acessados por meio de referências, seu tempo de vida é gerenciado pelo .NET Framework. As referências pendentes a uma instância específica são rastreadas e a instância é destruída somente quando não há mais referências. Uma variável de tipo de referência contém uma referência a um valor desse tipo, um valor de um tipo mais derivado ou um valor nulo. Um valor nulo não se refere a nada; não é possível fazer nada com um valor nulo, exceto atribuí-lo. A atribuição a uma variável de um tipo de referência cria uma cópia da referência em vez de uma cópia do valor referenciado. Para uma variável de um tipo de referência, o valor padrão é um valor nulo.
Os tipos de valor são armazenados diretamente na pilha, dentro de uma matriz ou dentro de outro tipo; seu armazenamento só pode ser acessado diretamente. Como os tipos de valor são armazenados diretamente em variáveis, seu tempo de vida é determinado pelo tempo de vida da variável que os contém. Quando o local que contém uma instância de tipo de valor é destruído, a instância de tipo de valor também é destruída. Os tipos de valor são sempre acessados diretamente; não é possível criar uma referência a um tipo de valor. A proibição de tal referência impossibilita a referência a uma instância de classe de valor que foi destruída. Como os tipos de valor são sempre NotInheritable, uma variável de um tipo de valor sempre contém um valor desse tipo. Por isso, o valor de um tipo de valor não pode ser um valor nulo, nem pode referenciar um objeto de um tipo mais derivado. A atribuição a uma variável de um tipo de valor cria uma cópia do valor que está sendo atribuído. Para uma variável de um tipo de valor, o valor padrão é o resultado da inicialização de cada membro variável do tipo para seu valor padrão.
O exemplo a seguir mostra a diferença entre tipos de referência e tipos de valor:
Class Class1
Public Value As Integer = 0
End Class
Module Test
Sub Main()
Dim val1 As Integer = 0
Dim val2 As Integer = val1
val2 = 123
Dim ref1 As Class1 = New Class1()
Dim ref2 As Class1 = ref1
ref2.Value = 123
Console.WriteLine("Values: " & val1 & ", " & val2)
Console.WriteLine("Refs: " & ref1.Value & ", " & ref2.Value)
End Sub
End Module
A saída do programa é:
Values: 0, 123
Refs: 123, 123
A atribuição à variável val2 local não afeta a variável val1 local porque ambas as variáveis locais são de um tipo de valor (o tipo Integer) e cada variável local de um tipo de valor tem seu próprio armazenamento. Por outro lado, a atribuição ref2.Value = 123; afeta o objeto que faz referência e ref2 ao ref1 objeto.
Uma coisa a observar sobre o sistema de tipos do .NET Framework é que, embora estruturas, enumerações e tipos primitivos (exceto ) Stringsejam tipos de valor, todos herdam de tipos de referência. Estruturas e os tipos primitivos herdam do tipo System.ValueTypede referência, que herda de Object. Tipos enumerados herdam do tipo System.Enumde referência, que herda de System.ValueType.
Tipos de valor anuláveis
Para tipos de valor, um ? modificador pode ser adicionado a um nome de tipo para representar a versão anulável desse tipo.
NullableTypeName
: NonArrayTypeName '?'
;
NullableNameModifier
: '?'
;
Um tipo de valor anulável pode conter os mesmos valores que a versão não anulável do tipo, bem como o valor nulo. Assim, para um tipo de valor anulável, atribuir Nothing a uma variável do tipo define o valor da variável como o valor nulo, não o valor zero do tipo de valor. Por exemplo:
Dim x As Integer = Nothing
Dim y As Integer? = Nothing
' Prints zero
Console.WriteLine(x)
' Prints nothing (because the value of y is the null value)
Console.WriteLine(y)
Uma variável também pode ser declarada como de um tipo de valor anulável colocando um modificador de tipo anulável no nome da variável. Para maior clareza, não é válido ter um modificador de tipo anulável em um nome de variável e um nome de tipo na mesma declaração. Como tipos anuláveis são implementados usando o tipo System.Nullable(Of T), o tipo T? é sinônimo do tipo System.Nullable(Of T)e os dois nomes podem ser usados de forma intercambiável. O ? modificador não pode ser colocado em um tipo que já seja anulável; portanto, não é possível declarar o tipo Integer?? ou System.Nullable(Of Integer)?.
Um tipo T? de valor anulável tem os membros, System.Nullable(Of T) bem como quaisquer operadores ou conversões levantadas do tipo T subjacente para o tipo T?. O levantamento copia operadores e conversões do tipo subjacente, na maioria dos casos substituindo tipos de valor anuláveis por tipos de valor não anuláveis. Isso permite que muitas das mesmas conversões e operações que se aplicam também T se apliquem T? .
Implementação da interface
Declarações de estrutura e classe podem declarar que implementam um conjunto de tipos de interface por meio de uma ou mais Implements cláusulas.
TypeImplementsClause
: 'Implements' TypeImplements StatementTerminator
;
TypeImplements
: NonArrayTypeName ( Comma NonArrayTypeName )*
;
Todos os tipos especificados na Implements cláusula devem ser interfaces e o tipo deve implementar todos os membros das interfaces. Por exemplo:
Interface ICloneable
Function Clone() As Object
End Interface
Interface IComparable
Function CompareTo(other As Object) As Integer
End Interface
Structure ListEntry
Implements ICloneable, IComparable
...
Public Function Clone() As Object Implements ICloneable.Clone
...
End Function
Public Function CompareTo(other As Object) As Integer _
Implements IComparable.CompareTo
...
End Function
End Structure
Um tipo que implementa uma interface também implementa implicitamente todas as interfaces base da interface. Isso é verdadeiro mesmo que o tipo não liste explicitamente todas as interfaces base na Implements cláusula. Neste exemplo, a TextBox estrutura implementa tanto quanto IControlITextBox.
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Structure TextBox
Implements ITextBox
...
Public Sub Paint() Implements ITextBox.Paint
...
End Sub
Public Sub SetText(text As String) Implements ITextBox.SetText
...
End Sub
End Structure
Declarar que um tipo implementa uma interface por si só não declara nada no espaço de declaração do tipo. Portanto, é válido implementar duas interfaces com um método com o mesmo nome.
Os tipos não podem implementar um parâmetro de tipo por conta própria, embora ele possa envolver os parâmetros de tipo que estão no escopo.
Class C1(Of V)
Implements V ' Error, can't implement type parameter directly
Implements IEnumerable(Of V) ' OK, not directly implementing
...
End Class
Interfaces genéricas podem ser implementadas várias vezes usando argumentos de tipo diferentes. No entanto, um tipo genérico não poderá implementar uma interface genérica usando um parâmetro de tipo se o parâmetro de tipo fornecido (independentemente das restrições de tipo) puder se sobrepor a outra implementação dessa interface. Por exemplo:
Interface I1(Of T)
End Interface
Class C1
Implements I1(Of Integer)
Implements I1(Of Double) ' OK, no overlap
End Class
Class C2(Of T)
Implements I1(Of Integer)
Implements I1(Of T) ' Error, T could be Integer
End Class
Tipos primitivos
Os tipos primitivos são identificados por meio de palavras-chave, que são aliases para tipos predefinidos no System namespace. Um tipo primitivo é completamente indistinguível do tipo que aliases: escrever a palavra Byte reservada é exatamente o mesmo que escrever System.Byte. Tipos primitivos também são conhecidos como tipos intrínsecos.
PrimitiveTypeName
: NumericTypeName
| 'Boolean'
| 'Date'
| 'Char'
| 'String'
;
NumericTypeName
: IntegralTypeName
| FloatingPointTypeName
| 'Decimal'
;
IntegralTypeName
: 'Byte' | 'SByte' | 'UShort' | 'Short' | 'UInteger'
| 'Integer' | 'ULong' | 'Long'
;
FloatingPointTypeName
: 'Single' | 'Double'
;
Como um tipo primitivo aliasa um tipo regular, cada tipo primitivo tem membros. Por exemplo, Integer tem os membros declarados em System.Int32. Literais podem ser tratados como instâncias de seus tipos correspondentes.
Os tipos primitivos diferem de outros tipos de estrutura, pois permitem determinadas operações adicionais:
Os tipos primitivos permitem que os valores sejam criados escrevendo literais. Por exemplo,
123Ié um literal do tipoInteger.É possível declarar constantes dos tipos primitivos.
Quando os operandos de uma expressão são todas constantes de tipo primitivo, é possível que o compilador avalie a expressão em tempo de compilação. Essa expressão é conhecida como uma expressão constante.
O Visual Basic define os seguintes tipos primitivos:
Os tipos
Bytede valor integral (inteiro sem sinal de 1 byte),SByte(inteiro com sinal de 1 byte),UShort(inteiro sem sinal de 2 bytes),Short(inteiro com sinal de 2 bytes),UInteger(inteiro sem sinal de 4 bytes),Integer(inteiro com sinal de 4 bytes), (inteiro com sinal de 4 bytes),ULong(inteiro sem sinal de 8 bytes) eLong(inteiro com sinal de 8 bytes). Esses tipos são mapeados paraSystem.Byte, ,System.SByte,System.UInt16,System.Int16System.UInt32,System.Int32eSystem.UInt64System.Int64, respectivamente. O valor padrão de um tipo integral é equivalente ao literal0.Os tipos
Singlede valor de ponto flutuante (ponto flutuante de 4 bytes) eDouble(ponto flutuante de 8 bytes). Esses tipos são mapeados paraSystem.SingleeSystem.Double, respectivamente. O valor padrão de um tipo de ponto flutuante é equivalente ao literal0.O
Decimaltipo (valor decimal de 16 bytes), que é mapeado paraSystem.Decimal. O valor padrão de decimal é equivalente ao literal0D.O
Booleantipo de valor, que representa um valor de verdade, normalmente é o resultado de uma operação relacional ou lógica. O literal é do tipoSystem.Boolean. O valor padrão doBooleantipo é equivalente ao literalFalse.O
Datetipo de valor, que representa uma data e/ou uma hora e mapeia paraSystem.DateTime. O valor padrão doDatetipo é equivalente ao literal# 01/01/0001 12:00:00AM #.O
Chartipo de valor, que representa um único caractere Unicode e mapeia paraSystem.Char. O valor padrão doChartipo é equivalente à expressãoChrW(0)constante.O
Stringtipo de referência, que representa uma sequência de caracteres Unicode e mapeia paraSystem.String. O valor padrão doStringtipo é um valor nulo.
Enumerações
Enumerações são tipos de System.Enum valor que herdam e representam simbolicamente um conjunto de valores de um dos tipos integrais primitivos.
EnumDeclaration
: Attributes? TypeModifier* 'Enum' Identifier
( 'As' NonArrayTypeName )? StatementTerminator
EnumMemberDeclaration+
'End' 'Enum' StatementTerminator
;
Para um tipo Ede enumeração, o valor padrão é o valor produzido pela expressão CType(0, E).
O tipo subjacente de uma enumeração deve ser um tipo integral que possa representar todos os valores de enumerador definidos na enumeração. Se um tipo subjacente for especificado, ele deverá ser Byte, , SByte, UShort, Short, , UInteger, Integer, ULong, Longou um de seus tipos correspondentes no System namespace. Se nenhum tipo subjacente for especificado explicitamente, o padrão será Integer.
O exemplo a seguir declara uma enumeração com um tipo subjacente de Long:
Enum Color As Long
Red
Green
Blue
End Enum
Um desenvolvedor pode optar por usar um tipo subjacente de Long, como no exemplo, para habilitar o uso de valores que estão no intervalo de Long, mas não no intervalo de Integer, ou para preservar essa opção para o futuro.
Membros de enumeração
Os membros de uma enumeração são os valores enumerados declarados na enumeração e os membros herdados da classe System.Enum.
O escopo de um membro de enumeração é o corpo da declaração de enumeração. Isso significa que, fora de uma declaração de enumeração, um membro de enumeração deve ser sempre qualificado (a menos que o tipo seja importado especificamente para um namespace por meio de uma importação de namespace).
A ordem de declaração para declarações de membro de enumeração é significativa quando valores de expressão constante são omitidos. Os membros de enumeração só têm Public acesso implicitamente; nenhum modificador de acesso é permitido em declarações de membro de enumeração.
EnumMemberDeclaration
: Attributes? Identifier ( Equals ConstantExpression )? StatementTerminator
;
Valores de enumeração
Os valores enumerados em uma lista de membros de enumeração são declarados como constantes digitadas como o tipo de enumeração subjacente e podem aparecer sempre que as constantes forem necessárias. Uma definição de membro de enumeração fornece = ao membro associado o valor indicado pela expressão constante. A expressão constante deve ser avaliada como um tipo integral que seja implicitamente conversível para o tipo subjacente e deve estar dentro do intervalo de valores que podem ser representados pelo tipo subjacente. O exemplo a seguir está em erro porque os valores constantes 1.52.33.3 e não são implicitamente conversíveis para o tipo Long integral subjacente com semântica estrita.
Option Strict On
Enum Color As Long
Red = 1.5
Green = 2.3
Blue = 3.3
End Enum
Vários membros de enumeração podem compartilhar o mesmo valor associado, conforme mostrado abaixo:
Enum Color
Red
Green
Blue
Max = Blue
End Enum
O exemplo mostra uma enumeração que tem dois membros de enumeração e BlueMax que têm o mesmo valor associado.
Se a primeira definição de valor do enumerador na enumeração não tiver nenhum inicializador, o valor da constante correspondente será 0. Uma definição de valor de enumeração sem um inicializador fornece ao enumerador o valor obtido aumentando o valor do valor de enumeração anterior por 1. Esse valor aumentado deve estar dentro do intervalo de valores que podem ser representados pelo tipo subjacente.
Enum Color
Red
Green = 10
Blue
End Enum
Module Test
Sub Main()
Console.WriteLine(StringFromColor(Color.Red))
Console.WriteLine(StringFromColor(Color.Green))
Console.WriteLine(StringFromColor(Color.Blue))
End Sub
Function StringFromColor(c As Color) As String
Select Case c
Case Color.Red
Return String.Format("Red = " & CInt(c))
Case Color.Green
Return String.Format("Green = " & CInt(c))
Case Color.Blue
Return String.Format("Blue = " & CInt(c))
Case Else
Return "Invalid color"
End Select
End Function
End Module
O exemplo acima imprime os valores de enumeração e seus valores associados. A saída é:
Red = 0
Green = 10
Blue = 11
Os motivos para os valores são os seguintes:
O valor
Redde enumeração é atribuído automaticamente ao valor0(já que ele não tem inicializador e é o primeiro membro de valor de enumeração).O valor
Greende enumeração recebe explicitamente o valor10.O valor
Bluede enumeração recebe automaticamente o valor um maior que o valor de enumeração que o precede textuamente.
A expressão constante pode não usar direta ou indiretamente o valor de seu próprio valor de enumeração associado (ou seja, a circularidade na expressão constante não é permitida). O exemplo a seguir é inválido porque as declarações são AB circulares.
Enum Circular
A = B
B
End Enum
A depende explicitamente B e B depende implicitamente A .
Aulas
Uma classe é uma estrutura de dados que pode conter membros de dados (constantes, variáveis e eventos), membros da função (métodos, propriedades, indexadores, operadores e construtores) e tipos aninhados. Classes são tipos de referência.
ClassDeclaration
: Attributes? ClassModifier* 'Class' Identifier TypeParameterList? StatementTerminator
ClassBase?
TypeImplementsClause*
ClassMemberDeclaration*
'End' 'Class' StatementTerminator
;
ClassModifier
: TypeModifier
| 'MustInherit'
| 'NotInheritable'
| 'Partial'
;
O exemplo a seguir mostra uma classe que contém cada tipo de membro:
Class AClass
Public Sub New()
Console.WriteLine("Constructor")
End Sub
Public Sub New(value As Integer)
MyVariable = value
Console.WriteLine("Constructor")
End Sub
Public Const MyConst As Integer = 12
Public MyVariable As Integer = 34
Public Sub MyMethod()
Console.WriteLine("MyClass.MyMethod")
End Sub
Public Property MyProperty() As Integer
Get
Return MyVariable
End Get
Set (value As Integer)
MyVariable = value
End Set
End Property
Default Public Property Item(index As Integer) As Integer
Get
Return 0
End Get
Set (value As Integer)
Console.WriteLine("Item(" & index & ") = " & value)
End Set
End Property
Public Event MyEvent()
Friend Class MyNestedClass
End Class
End Class
O exemplo a seguir mostra os usos desses membros:
Module Test
' Event usage.
Dim WithEvents aInstance As AClass
Sub Main()
' Constructor usage.
Dim a As AClass = New AClass()
Dim b As AClass = New AClass(123)
' Constant usage.
Console.WriteLine("MyConst = " & AClass.MyConst)
' Variable usage.
a.MyVariable += 1
Console.WriteLine("a.MyVariable = " & a.MyVariable)
' Method usage.
a.MyMethod()
' Property usage.
a.MyProperty += 1
Console.WriteLine("a.MyProperty = " & a.MyProperty)
a(1) = 1
' Event usage.
aInstance = a
End Sub
Sub MyHandler() Handles aInstance.MyEvent
Console.WriteLine("Test.MyHandler")
End Sub
End Module
Há dois modificadores específicos MustInherit de classe e NotInheritable. É inválido especificar ambos.
Especificação base de classe
Uma declaração de classe pode incluir uma especificação de tipo base que define o tipo base direto da classe.
ClassBase
: 'Inherits' NonArrayTypeName StatementTerminator
;
Se uma declaração de classe não tiver nenhum tipo base explícito, o tipo base direta será implicitamente Object. Por exemplo:
Class Base
End Class
Class Derived
Inherits Base
End Class
Os tipos base não podem ser um parâmetro de tipo por conta própria, embora possa envolver os parâmetros de tipo que estão no escopo.
Class C1(Of V)
End Class
Class C2(Of V)
Inherits V ' Error, type parameter used as base class
End Class
Class C3(Of V)
Inherits C1(Of V) ' OK: not directly inheriting from V.
End Class
As classes só podem derivar de classes e de Object classes. É inválido para uma classe derivar de System.ValueType, System.Enum, System.MulticastDelegateSystem.Arrayou System.Delegate. Uma classe genérica não pode derivar de System.Attribute ou de uma classe que deriva dela.
Cada classe tem exatamente uma classe base direta e a circularidade na derivação é proibida. Não é possível derivar de uma NotInheritable classe e o domínio de acessibilidade da classe base deve ser igual ou um superconjunto do domínio de acessibilidade da própria classe.
Membros da classe
Os membros de uma classe consistem nos membros introduzidos por suas declarações de membro de classe e os membros herdados de sua classe base direta.
ClassMemberDeclaration
: NonModuleDeclaration
| EventMemberDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
| OperatorDeclaration
;
Uma declaração de membro de classe pode ter Public, Protected, Friend, Protected Friendou Private acesso. Quando uma declaração de membro de classe não inclui um modificador de acesso, a declaração usa o padrão de Public acesso, a menos que seja uma declaração de variável; nesse caso, ela usa como padrão o Private acesso.
O escopo de um membro de classe é o corpo da classe no qual a declaração de membro ocorre, além da lista de restrições dessa classe (se ela for genérica e tiver restrições). Se o membro tiver Friend acesso, seu escopo se estenderá ao corpo da classe de qualquer classe derivada no mesmo programa ou em qualquer assembly que tenha acesso Friend , e se o membro tiver Public, Protectedou Protected Friend acesso, seu escopo se estenderá ao corpo da classe de qualquer classe derivada em qualquer programa.
Estruturas
Estruturas são tipos de valor que herdam de System.ValueType. As estruturas são semelhantes às classes em que representam estruturas de dados que podem conter membros de dados e membros da função. Ao contrário das classes, no entanto, as estruturas não exigem alocação de heap.
StructureDeclaration
: Attributes? StructureModifier* 'Structure' Identifier
TypeParameterList? StatementTerminator
TypeImplementsClause*
StructMemberDeclaration*
'End' 'Structure' StatementTerminator
;
StructureModifier
: TypeModifier
| 'Partial'
;
No caso de classes, é possível que duas variáveis referenciem o mesmo objeto e, portanto, as operações em uma variável afetem o objeto referenciado pela outra variável. Com estruturas, cada uma das variáveis tem sua própria cópia dos nãoShared dados, portanto, não é possível que as operações em um afetem a outra, como ilustra o exemplo a seguir:
Structure Point
Public x, y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
End Structure
Dada a declaração acima, o código a seguir gera o valor 10:
Module Test
Sub Main()
Dim a As New Point(10, 10)
Dim b As Point = a
a.x = 100
Console.WriteLine(b.x)
End Sub
End Module
A atribuição de a para b cria uma cópia do valor e b , portanto, não é afetada pela atribuição a a.x. Se Point tivesse sido declarado como uma classe, a saída seria 100 porque a e b fariam referência ao mesmo objeto.
Membros da estrutura
Os membros de uma estrutura são os membros introduzidos por suas declarações de membro de estrutura e os membros herdados.System.ValueType
StructMemberDeclaration
: NonModuleDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| EventMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
| OperatorDeclaration
;
Cada estrutura tem implicitamente um Public construtor de instância sem parâmetros que produz o valor padrão da estrutura. Como resultado, não é possível que uma declaração de tipo de estrutura declare um construtor de instância sem parâmetros. No entanto, um tipo de estrutura tem permissão para declarar construtores de instância parametrizados , como no exemplo a seguir:
Structure Point
Private x, y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
End Structure
Dada a declaração acima, as instruções a seguir criam uma Point com x e y inicializadas como zero.
Dim p1 As Point = New Point()
Dim p2 As Point = New Point(0, 0)
Como as estruturas contêm diretamente seus valores de campo (em vez de referências a esses valores), as estruturas não podem conter campos que se referenciam direta ou indiretamente. Por exemplo, o código a seguir não é válido:
Structure S1
Dim f1 As S2
End Structure
Structure S2
' This would require S1 to contain itself.
Dim f1 As S1
End Structure
Normalmente, uma declaração de membro de estrutura pode ter Publicapenas , Friendou Private acesso, mas ao substituir membros herdados Objecte ProtectedProtected Friend o acesso também pode ser usado. Quando uma declaração de membro de estrutura não inclui um modificador de acesso, a declaração usa como padrão o Public acesso. O escopo de um membro declarado por uma estrutura é o corpo da estrutura no qual a declaração ocorre, além das restrições dessa estrutura (se ela era genérica e tinha restrições).
Módulos Padrão
Um módulo padrão é um tipo cujos membros estão implicitamente Shared e com escopo para o espaço de declaração do namespace que contém o módulo padrão, em vez de apenas para a própria declaração de módulo padrão. Os módulos padrão podem nunca ser instanciados. É um erro declarar uma variável de um tipo de módulo padrão.
ModuleDeclaration
: Attributes? TypeModifier* 'Module' Identifier StatementTerminator
ModuleMemberDeclaration*
'End' 'Module' StatementTerminator
;
Um membro de um módulo padrão tem dois nomes totalmente qualificados, um sem o nome do módulo padrão e outro com o nome do módulo padrão. Mais de um módulo padrão em um namespace pode definir um membro com um nome específico; As referências não qualificadas ao nome fora de qualquer módulo são ambíguas. Por exemplo:
Namespace N1
Module M1
Sub S1()
End Sub
Sub S2()
End Sub
End Module
Module M2
Sub S2()
End Sub
End Module
Module M3
Sub Main()
S1() ' Valid: Calls N1.M1.S1.
N1.S1() ' Valid: Calls N1.M1.S1.
S2() ' Not valid: ambiguous.
N1.S2() ' Not valid: ambiguous.
N1.M2.S2() ' Valid: Calls N1.M2.S2.
End Sub
End Module
End Namespace
Um módulo só pode ser declarado em um namespace e pode não estar aninhado em outro tipo. Os módulos padrão podem não implementar interfaces, eles derivam Objectimplicitamente e têm apenas Shared construtores.
Membros do módulo Standard
Os membros de um módulo padrão são os membros introduzidos por suas declarações de membro e os membros herdados.Object Os módulos padrão podem ter qualquer tipo de membro, exceto construtores de instância. Todos os membros do tipo de módulo padrão são implicitamente Shared.
ModuleMemberDeclaration
: NonModuleDeclaration
| VariableMemberDeclaration
| ConstantMemberDeclaration
| EventMemberDeclaration
| MethodMemberDeclaration
| PropertyMemberDeclaration
| ConstructorMemberDeclaration
;
Normalmente, uma declaração de membro do módulo padrão pode ter Publicapenas, Friendou Private acesso, mas ao substituir membros herdados, Objectos modificadores de acesso e efetuados ProtectedProtected Friend podem ser especificados. Quando uma declaração de membro do módulo padrão não inclui um modificador de acesso, a declaração usa como padrão o Public acesso, a menos que seja uma variável, que usa como padrão o Private acesso.
Como observado anteriormente, o escopo de um membro do módulo padrão é a declaração que contém a declaração de módulo padrão. Os membros herdados Object não estão incluídos neste escopo especial; esses membros não têm escopo e devem ser sempre qualificados com o nome do módulo. Se o membro tiver Friend acesso, seu escopo se estenderá somente aos membros do namespace declarados no mesmo programa ou assemblies que receberam Friend acesso.
Interfaces
Interfaces são tipos de referência que outros tipos implementam para garantir que dão suporte a determinados métodos. Uma interface nunca é criada diretamente e não tem representação real. Outros tipos devem ser convertidos em um tipo de interface. Uma interface define um contrato. Uma classe ou estrutura que implementa uma interface deve aderir ao seu contrato.
InterfaceDeclaration
: Attributes? TypeModifier* 'Interface' Identifier
TypeParameterList? StatementTerminator
InterfaceBase*
InterfaceMemberDeclaration*
'End' 'Interface' StatementTerminator
;
O exemplo a seguir mostra uma interface que contém uma propriedade Itempadrão, um evento E, um método Fe uma propriedade P:
Interface IExample
Default Property Item(index As Integer) As String
Event E()
Sub F(value As Integer)
Property P() As String
End Interface
As interfaces podem empregar várias heranças. No exemplo a seguir, a interface IComboBox herda de ambos ITextBox e IListBox:
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Interface IListBox
Inherits IControl
Sub SetItems(items() As String)
End Interface
Interface IComboBox
Inherits ITextBox, IListBox
End Interface
Classes e estruturas podem implementar várias interfaces. No exemplo a seguir, a classe EditBox deriva da classe Control e implementa ambos IControl e IDataBound:
Interface IDataBound
Sub Bind(b As Binder)
End Interface
Public Class EditBox
Inherits Control
Implements IControl, IDataBound
Public Sub Paint() Implements IControl.Paint
...
End Sub
Public Sub Bind(b As Binder) Implements IDataBound.Bind
...
End Sub
End Class
Herança de interface
As interfaces base de uma interface são as interfaces base explícitas e suas interfaces base. Em outras palavras, o conjunto de interfaces de base é o fechamento transitivo completo das interfaces de base explícitas, das interfaces de base explícitas delas e assim por diante. Se uma declaração de Object interface não tiver uma base de interface explícita, não haverá nenhuma interface base para o tipo – as interfaces não herdam (embora tenham uma conversão de ampliação para Object).
InterfaceBase
: 'Inherits' InterfaceBases StatementTerminator
;
InterfaceBases
: NonArrayTypeName ( Comma NonArrayTypeName )*
;
No exemplo a seguir, as interfaces base são IComboBoxIControl, ITextBoxe IListBox.
Interface IControl
Sub Paint()
End Interface
Interface ITextBox
Inherits IControl
Sub SetText(text As String)
End Interface
Interface IListBox
Inherits IControl
Sub SetItems(items() As String)
End Interface
Interface IComboBox
Inherits ITextBox, IListBox
End Interface
Uma interface herda todos os membros das suas interfaces de base. Em outras palavras, a interface IComboBox acima herda os membros SetText e SetItems, bem como Paint.
Uma classe ou estrutura que implementa uma interface também implementa implicitamente todas as interfaces base da interface.
Se uma interface aparecer mais de uma vez no fechamento transitivo das interfaces base, ela só contribuirá com seus membros para a interface derivada uma vez. Um tipo que implementa a interface derivada só precisa implementar os métodos da interface base definida por multiplicação uma vez. No exemplo a seguir, Paint só precisa ser implementado uma vez, mesmo que a classe implemente IComboBox e IControl.
Class ComboBox
Implements IControl, IComboBox
Sub SetText(text As String) Implements IComboBox.SetText
End Sub
Sub SetItems(items() As String) Implements IComboBox.SetItems
End Sub
Sub Print() Implements IComboBox.Paint
End Sub
End Class
Uma Inherits cláusula não tem efeito sobre outras Inherits cláusulas. No exemplo a seguir, IDerived deve qualificar o nome com INestedIBase.
Interface IBase
Interface INested
Sub Nested()
End Interface
Sub Base()
End Interface
Interface IDerived
Inherits IBase, INested ' Error: Must specify IBase.INested.
End Interface
O domínio de acessibilidade de uma interface base deve ser o mesmo ou um superconjunto do domínio de acessibilidade da própria interface.
Membros da interface
Os membros de uma interface consistem nos membros introduzidos por suas declarações de membro e os membros herdados de suas interfaces base.
InterfaceMemberDeclaration
: NonModuleDeclaration
| InterfaceEventMemberDeclaration
| InterfaceMethodMemberDeclaration
| InterfacePropertyMemberDeclaration
;
Embora as interfaces não herdem membros de Object, porque cada classe ou estrutura que implementa uma interface herda Object, os membros de , incluindo métodos de Objectextensão, são considerados membros de uma interface e podem ser chamados em uma interface diretamente sem exigir uma conversão para Object. Por exemplo:
Interface I1
End Interface
Class C1
Implements I1
End Class
Module Test
Sub Main()
Dim i As I1 = New C1()
Dim h As Integer = i.GetHashCode()
End Sub
End Module
Membros de uma interface com o mesmo nome que membros de Object membros de sombra Object implícita. Somente tipos aninhados, métodos, propriedades e eventos podem ser membros de uma interface. Métodos e propriedades podem não ter um corpo. Os membros da interface são implicitamente Public e podem não especificar um modificador de acesso. O escopo de um membro declarado em uma interface é o corpo da interface no qual a declaração ocorre, além da lista de restrições dessa interface (se ela for genérica e tiver restrições).
matrizes
Uma matriz é um tipo de referência que contém variáveis acessadas por meio de índices correspondentes de uma forma um-para-um com a ordem das variáveis na matriz. As variáveis contidas em uma matriz, também chamadas de elementos da matriz, devem ser todas do mesmo tipo e esse tipo é chamado de tipo de elemento da matriz.
ArrayTypeName
: NonArrayTypeName ArrayTypeModifiers
;
ArrayTypeModifiers
: ArrayTypeModifier+
;
ArrayTypeModifier
: OpenParenthesis RankList? CloseParenthesis
;
RankList
: Comma*
;
ArrayNameModifier
: ArrayTypeModifiers
| ArraySizeInitializationModifier
;
Os elementos de uma matriz surgem quando uma instância de matriz é criada e deixam de existir quando a instância da matriz é destruída. Cada elemento de uma matriz é inicializado para o valor padrão de seu tipo. O tipo System.Array é o tipo base de todos os tipos de matriz e pode não ser instanciado. Cada tipo de matriz herda os membros declarados pelo System.Array tipo e é conversível para ele (e Object). Um tipo de matriz unidimensional com elemento T também implementa as interfaces System.Collections.Generic.IList(Of T) e IReadOnlyList(Of T); se T for um tipo de referência, o tipo de matriz também implementa IList(Of U) e IReadOnlyList(Of U) para qualquer U um que tenha uma conversão de referência de ampliação de T.
Uma matriz tem uma classificação que determina o número de índices associados a cada elemento de matriz. A classificação de uma matriz determina o número de dimensões da matriz. Por exemplo, uma matriz com uma classificação de um é chamada de matriz unidimensional e uma matriz com uma classificação maior que uma é chamada de matriz multidimensional.
O exemplo a seguir cria uma matriz unidimensional de valores inteiros, inicializa os elementos da matriz e imprime cada um deles:
Module Test
Sub Main()
Dim arr(5) As Integer
Dim i As Integer
For i = 0 To arr.Length - 1
arr(i) = i * i
Next i
For i = 0 To arr.Length - 1
Console.WriteLine("arr(" & i & ") = " & arr(i))
Next i
End Sub
End Module
O programa gera o seguinte:
arr(0) = 0
arr(1) = 1
arr(2) = 4
arr(3) = 9
arr(4) = 16
arr(5) = 25
Cada dimensão de uma matriz tem um comprimento associado. Os comprimentos de dimensão não fazem parte do tipo da matriz, mas sim são estabelecidos quando uma instância do tipo de matriz é criada em tempo de execução. O comprimento de uma dimensão determina o intervalo válido de índices para essa dimensão: para uma dimensão de comprimento N, os índices podem variar de zero a N-1. Se uma dimensão for de comprimento zero, não haverá índices válidos para essa dimensão. O número total de elementos em uma matriz é o produto dos comprimentos de cada dimensão na matriz. Se qualquer uma das dimensões de uma matriz tiver um comprimento igual a zero, a matriz será considerada vazia. O tipo de elemento de uma matriz pode ser qualquer tipo.
Os tipos de matriz são especificados adicionando um modificador a um nome de tipo existente. O modificador consiste em um parêntese esquerdo, um conjunto de zero ou mais vírgulas e um parêntese direito. O tipo modificado é o tipo de elemento da matriz e o número de dimensões é o número de vírgulas mais uma. Se mais de um modificador for especificado, o tipo de elemento da matriz será uma matriz. Os modificadores são lidos da esquerda para a direita, com o modificador mais à esquerda sendo a matriz mais externa. No exemplo
Module Test
Dim arr As Integer(,)(,,)()
End Module
o tipo de arr elemento é uma matriz bidimensional de matrizes tridimensionais de matrizes unidimensionais de Integer.
Uma variável também pode ser declarada como de um tipo de matriz colocando um modificador de tipo de matriz ou um modificador de inicialização de tamanho de matriz no nome da variável. Nesse caso, o tipo de elemento de matriz é o tipo fornecido na declaração e as dimensões da matriz são determinadas pelo modificador de nome da variável. Para maior clareza, não é válido ter um modificador de tipo de matriz em um nome de variável e um nome de tipo na mesma declaração.
O exemplo a seguir mostra uma variedade de declarações de variáveis locais que usam tipos de matriz com Integer o tipo de elemento:
Module Test
Sub Main()
Dim a1() As Integer ' Declares 1-dimensional array of integers.
Dim a2(,) As Integer ' Declares 2-dimensional array of integers.
Dim a3(,,) As Integer ' Declares 3-dimensional array of integers.
Dim a4 As Integer() ' Declares 1-dimensional array of integers.
Dim a5 As Integer(,) ' Declares 2-dimensional array of integers.
Dim a6 As Integer(,,) ' Declares 3-dimensional array of integers.
' Declare 1-dimensional array of 2-dimensional arrays of integers
Dim a7()(,) As Integer
' Declare 2-dimensional array of 1-dimensional arrays of integers.
Dim a8(,)() As Integer
Dim a9() As Integer() ' Not allowed.
End Sub
End Module
Um modificador de nome de tipo de matriz se estende a todos os conjuntos de parênteses que o seguem. Isso significa que, nas situações em que um conjunto de argumentos entre parênteses é permitido após um nome de tipo, não é possível especificar os argumentos para um nome de tipo de matriz. Por exemplo:
Module Test
Sub Main()
' This calls the Integer constructor.
Dim x As New Integer(3)
' This declares a variable of Integer().
Dim y As Integer()
' This gives an error.
' Array sizes can not be specified in a type name.
Dim z As Integer()(3)
End Sub
End Module
No último caso, (3) é interpretado como parte do nome do tipo em vez de como um conjunto de argumentos de construtor.
Delegados
Um delegado é um tipo de referência que se refere a um Shared método de um tipo ou a um método de instância de um objeto.
DelegateDeclaration
: Attributes? TypeModifier* 'Delegate' MethodSignature StatementTerminator
;
MethodSignature
: SubSignature
| FunctionSignature
;
O equivalente mais próximo de um delegado em outras linguagens é um ponteiro de função, mas enquanto um ponteiro de função só pode referenciar Shared funções, um delegado pode referenciar métodos de instância e ambos Shared . No último caso, o delegado armazena não apenas uma referência ao ponto de entrada do método, mas também uma referência à instância de objeto com a qual invocar o método.
A declaração delegada pode não ter uma Handles cláusula, uma Implements cláusula, um corpo do método ou um End constructo. A lista de parâmetros da declaração delegada pode não conter Optional ou ParamArray parâmetros. O domínio de acessibilidade do tipo de retorno e dos tipos de parâmetro deve ser igual ou um superconjunto do domínio de acessibilidade do próprio delegado.
Os membros de um delegado são os membros herdados da classe System.Delegate. Um delegado também define os seguintes métodos:
Um construtor que usa dois parâmetros, um do tipo
Objecte um do tipoSystem.IntPtr.Um
Invokemétodo que tem a mesma assinatura que o delegado.Um
BeginInvokemétodo cuja assinatura é a assinatura delegada, com três diferenças. Primeiro, o tipo de retorno é alterado paraSystem.IAsyncResult. Em segundo lugar, dois parâmetros adicionais são adicionados ao final da lista de parâmetros: o primeiro do tipoSystem.AsyncCallbacke o segundo do tipoObject. E, por fim, todos osByRefparâmetros são alterados para seremByVal.Um
EndInvokemétodo cujo tipo de retorno é o mesmo que o delegado. Os parâmetros do método são apenas os parâmetros delegados exatamente que sãoByRefparâmetros, na mesma ordem em que ocorrem na assinatura delegada. Além desses parâmetros, há um parâmetro adicional de tipoSystem.IAsyncResultno final da lista de parâmetros.
Há três etapas para definir e usar delegados: declaração, instanciação e invocação.
Os delegados são declarados usando a sintaxe da declaração de delegado. O exemplo a seguir declara um delegado nomeado SimpleDelegate que não aceita argumentos:
Delegate Sub SimpleDelegate()
O exemplo a seguir cria uma SimpleDelegate instância e, em seguida, chama-a imediatamente:
Module Test
Sub F()
System.Console.WriteLine("Test.F")
End Sub
Sub Main()
Dim d As SimpleDelegate = AddressOf F
d()
End Sub
End Module
Não há muito sentido em instanciar um delegado para um método e, em seguida, chamar imediatamente por meio do delegado, pois seria mais simples chamar o método diretamente. Os delegados mostram sua utilidade quando seu anonimato é usado. O exemplo a seguir mostra um MultiCall método que chama repetidamente uma SimpleDelegate instância:
Sub MultiCall(d As SimpleDelegate, count As Integer)
Dim i As Integer
For i = 0 To count - 1
d()
Next i
End Sub
Não é importante para o método qual MultiCall é o método de destino, qual é a SimpleDelegate acessibilidade que esse método tem ou se o método é Shared ou não. Tudo o que importa é que a assinatura do método de destino é compatível com SimpleDelegate.
Tipos parciais
Declarações de classe e estrutura podem ser declarações parciais . Uma declaração parcial pode ou não descrever totalmente o tipo declarado dentro da declaração. Em vez disso, a declaração do tipo pode ser distribuída entre várias declarações parciais dentro do programa; tipos parciais não podem ser declarados entre os limites do programa. Uma declaração de tipo parcial especifica o Partial modificador na declaração. Em seguida, quaisquer outras declarações no programa para um tipo com o mesmo nome totalmente qualificado serão mescladas com a declaração parcial em tempo de compilação para formar uma declaração de tipo único. Por exemplo, o código a seguir declara uma única classe Test com membros Test.C1 e Test.C2.
a.vb:
Public Partial Class Test
Public Sub S1()
End Sub
End Class
b.vb:
Public Class Test
Public Sub S2()
End Sub
End Class
Ao combinar declarações de tipo parcial, pelo menos uma das declarações deve ter um Partial modificador, caso contrário, um erro de tempo de compilação será gerado.
Observação. Embora seja possível especificar Partial apenas uma declaração entre muitas declarações parciais, é melhor especifique-a em todas as declarações parciais. Na situação em que uma declaração parcial está visível, mas uma ou mais declarações parciais estão ocultas (como o caso da extensão do código gerado pela ferramenta), é aceitável deixar o Partial modificador fora da declaração visível, mas especifique-o nas declarações ocultas.
Somente classes e estruturas podem ser declaradas usando declarações parciais. A aridade de um tipo é considerada ao combinar declarações parciais: duas classes com o mesmo nome, mas números diferentes de parâmetros de tipo não são considerados declarações parciais do mesmo tempo. Declarações parciais podem especificar atributos, modificadores de classe, Inherits instrução ou Implements instrução. No momento da compilação, todas as partes das declarações parciais são combinadas e usadas como parte da declaração de tipo. Se houver conflitos entre atributos, modificadores, bases, interfaces ou membros do tipo, um erro de tempo de compilação resultará. Por exemplo:
Public Partial Class Test1
Implements IDisposable
End Class
Class Test1
Inherits Object
Implements IComparable
End Class
Public Partial Class Test2
End Class
Private Partial Class Test2
End Class
O exemplo anterior declara um tipo Test1 que é Public, herda e Object implementa System.IDisposable e System.IComparable. As declarações parciais de Test2 causarão um erro de tempo de compilação porque uma das declarações diz que Test2 é Public e outra diz que Test2 é Private.
Tipos parciais com parâmetros de tipo podem declarar restrições e variação para os parâmetros de tipo, mas as restrições e a variação de cada declaração parcial devem corresponder. Portanto, as restrições e a variação são especiais, pois não são combinadas automaticamente como outros modificadores:
Partial Public Class List(Of T As IEnumerable)
End Class
' Error: Constraints on T don't match
Class List(Of T As IComparable)
End Class
O fato de um tipo ser declarado usando várias declarações parciais não afeta as regras de pesquisa de nome dentro do tipo. Como resultado, uma declaração de tipo parcial pode usar membros declarados em outras declarações de tipo parcial ou pode implementar métodos em interfaces declaradas em outras declarações de tipo parcial. Por exemplo:
Public Partial Class Test1
Implements IDisposable
Private IsDisposed As Boolean = False
End Class
Class Test1
Private Sub Dispose() Implements IDisposable.Dispose
If Not IsDisposed Then
...
End If
End Sub
End Class
Tipos aninhados também podem ter declarações parciais. Por exemplo:
Public Partial Class Test
Public Partial Class NestedTest
Public Sub S1()
End Sub
End Class
End Class
Public Partial Class Test
Public Partial Class NestedTest
Public Sub S2()
End Sub
End Class
End Class
Inicializadores dentro de uma declaração parcial ainda serão executados na ordem de declaração; no entanto, não há nenhuma ordem de execução garantida para inicializadores que ocorram em declarações parciais separadas.
Tipos construídos
Uma declaração de tipo genérico, por si só, não indica um tipo. Em vez disso, uma declaração de tipo genérico pode ser usada como um "blueprint" para formar muitos tipos diferentes aplicando argumentos de tipo. Um tipo genérico que tem argumentos de tipo aplicados a ele é chamado de tipo construído. Os argumentos de tipo em um tipo construído sempre devem satisfazer as restrições colocadas nos parâmetros de tipo aos que correspondem.
Um nome de tipo pode identificar um tipo construído mesmo que ele não especifique parâmetros de tipo diretamente. Isso pode ocorrer quando um tipo é aninhado dentro de uma declaração de classe genérica e o tipo de instância da declaração que contém é usado implicitamente para pesquisa de nome:
Class Outer(Of T)
Public Class Inner
End Class
' Type of i is the constructed type Outer(Of T).Inner
Public i As Inner
End Class
Um tipo C(Of T1,...,Tn) construído é acessível quando o tipo genérico e todos os argumentos de tipo estão acessíveis. Por exemplo, se o tipo C genérico for Public e todos os argumentos T1,...,Tn de tipo forem Public, o tipo construído será Public. Se o nome do tipo ou um dos argumentos de tipo for Private, no entanto, a acessibilidade do tipo construído será Private. Se um argumento de tipo do tipo construído for Protected e outro argumento de tipo for Friend, o tipo construído só será acessível na classe e suas subclasses neste assembly ou em qualquer assembly que tenha recebido Friend acesso. Em outras palavras, o domínio de acessibilidade de um tipo construído é a interseção dos domínios de acessibilidade de suas partes constituintes.
Observação. O fato de que o domínio de acessibilidade do tipo construído é a interseção de suas partes constituídas tem o interessante efeito colateral de definir um novo nível de acessibilidade. Um tipo construído que contém um elemento que é Protected e um elemento que só Friend pode ser acessado em contextos que podem acessar ambosFriendeProtected membros. No entanto, não há como expressar esse nível de acessibilidade no idioma, pois a acessibilidade Protected Friend significa que uma entidade pode ser acessada em um contexto que possa acessar membrosouProtectedmembrosFriend.
As interfaces base e implementadas e os membros de tipos construídos são determinados substituindo os argumentos de tipo fornecidos para cada ocorrência do parâmetro de tipo no tipo genérico.
Tipos abertos e tipos fechados
Um tipo construído para quem um ou mais argumentos de tipo são parâmetros de tipo de um tipo ou método que contém é chamado de tipo aberto. Isso ocorre porque alguns dos parâmetros de tipo do tipo ainda não são conhecidos, portanto, a forma real do tipo ainda não é totalmente conhecida. Por outro lado, um tipo genérico cujos argumentos de tipo são todos parâmetros não tipo é chamado de tipo fechado. A forma de um tipo fechado é sempre totalmente conhecida. Por exemplo:
Class Base(Of T, V)
End Class
Class Derived(Of V)
Inherits Base(Of Integer, V)
End Class
Class MoreDerived
Inherits Derived(Of Double)
End Class
O tipo Base(Of Integer, V) construído é um tipo aberto porque, embora o parâmetro T de tipo tenha sido fornecido, o parâmetro U de tipo foi fornecido outro parâmetro de tipo. Portanto, a forma completa do tipo ainda não é conhecida. O tipo Derived(Of Double)construído, no entanto, é um tipo fechado porque todos os parâmetros de tipo na hierarquia de herança foram fornecidos.
Os tipos abertos são definidos da seguinte maneira:
Um parâmetro de tipo é um tipo aberto.
Um tipo de matriz será um tipo aberto se o tipo de elemento for um tipo aberto.
Um tipo construído será um tipo aberto se um ou mais de seus argumentos de tipo forem um tipo aberto.
Um tipo fechado é um tipo que não é um tipo aberto.
Como o ponto de entrada do programa não pode estar em um tipo genérico, todos os tipos usados em tempo de execução serão tipos fechados.
Tipos especiais
O .NET Framework contém várias classes que são tratadas especialmente pelo .NET Framework e pela linguagem Visual Basic:
O tipo System.Void, que representa um tipo nulo no .NET Framework, pode ser referenciado diretamente somente em GetType expressões.
Os tipos System.RuntimeArgumentHandlee System.ArgIteratorSystem.TypedReference todos podem conter ponteiros para a pilha e, portanto, não podem aparecer no heap do .NET Framework. Portanto, eles não podem ser usados como tipos de elemento de matriz, tipos de retorno, tipos de campo, argumentos de tipo genérico, tipos anuláveis, ByRef tipos de parâmetro, o tipo de um valor que está sendo convertido Object em ou System.ValueType, o destino de uma chamada para membros de Object instância de ou System.ValueType, ou levantado em um fechamento.
Visual Basic language spec