DevTools

Cheatsheet Visual Basic

Linguagem da Microsoft para aplicações Windows, Office e automação

Voltar às linguagens
Visual Basic
112 cards encontrados
Categorias:
Versões:

Básico e Estrutura


12 cards
Estrutura do programa
Imports System

Module Program
    Sub Main(args As String())
        Console.WriteLine("Olá Mundo!")
        Console.Write("Nome: ")
        Dim nome = Console.ReadLine()
        Console.WriteLine($"Olá, {nome}!")
    End Sub
End Module

Um programa de consola VB.NET começa num Module com o método Sub Main. O Imports traz namespaces e o Console escreve no ecrã.

Operadores de comparação e lógicos
Dim x = 10

' Comparação (retornam Boolean):
Dim r1 = x = 10     ' True
Dim r2 = x <> 5     ' True (diferente)
Dim r3 = x >= 10    ' True

' Lógicos:
Dim ok = r1 And r2
Dim ok2 = r1 Or r3
Dim neg = Not r1

' Curto-circuito (recomendado):
If x > 0 AndAlso x < 100 Then
    Console.WriteLine("entre 0 e 100")
End If

AndAlso e OrElse são as versões de curto-circuito: param de avaliar assim que o resultado é conhecido, evitando erros e ganhando desempenho.

Option Strict e Option Infer
' No topo do ficheiro:
Option Strict On    ' proíbe conversões implícitas
Option Infer On     ' permite inferir tipos
Option Explicit On  ' obriga a declarar variáveis

' Com Option Strict On, isto dá erro:
' Dim n As Integer = "42"   ' erro!

' É preciso converter explicitamente:
Dim n As Integer = CInt("42")

' Inferência:
Dim lista = New List(Of String)()  ' tipo inferido

Option Strict On é uma boa prática: impede conversões automáticas que escondem bugs. Option Infer On deixa o compilador deduzir o tipo a partir do valor.

Variáveis com Dim
Dim nome As String = "VB.NET"
Dim idade As Integer = 30
Dim ativo As Boolean = True
Dim pi As Double = 3.14
Dim preco As Decimal = 99.99D

' Inferência de tipo (Option Infer On):
Dim x = 42        ' Integer
Dim s = "Olá"     ' String

Const MAX As Integer = 100

Declara-se variáveis com Dim. O tipo pode ser explícito (As Integer) ou inferido pelo valor. Const cria constantes imutáveis.

Concatenação de strings
Dim nome = "Ana"
Dim apelido = "Silva"

' Operador & (concatenação):
Dim completo = nome & " " & apelido

' Operador + (também funciona):
Dim s = "Olá " + nome

' Juntar com números:
Dim idade = 30
Dim msg = nome & " tem " & idade & " anos"

Console.WriteLine(completo)  ' Ana Silva

O operador & é o recomendado para concatenar, pois converte automaticamente números em texto. O + pode causar ambiguidade com a soma.

Comentários
' Isto é um comentário de linha

Dim x = 10  ' comentário no fim da linha

REM Também funciona (antigo, pouco usado)

''' <summary>
''' Comentário de documentação XML
''' </summary>
''' <param name="n">O número</param>
Function Dobro(n As Integer) As Integer
    Return n * 2
End Function

O apóstrofo ' inicia um comentário. Os comentários XML com ''' geram documentação IntelliSense para os métodos.

Tipos de dados
' Inteiros: Byte, Short, Integer, Long
' Decimais: Single, Double, Decimal
' Outros:  Boolean, Char, String, Date, Object

Dim n As Integer = 42
Dim d As Date = #2024-01-15#
Dim c As Char = "A"c
Dim o As Object = "qualquer coisa"

' Sufixos de literal:
Dim l = 100L      ' Long
Dim f = 1.5F      ' Single
Dim dec = 9.9D    ' Decimal

VB.NET usa os tipos do .NET. Literais de data ficam entre #, e sufixos como L, F e D indicam o tipo numérico.

Operador ternário If()
Dim idade = 20

' If(condição, valorSeVerdadeiro, valorSeFalso)
Dim estado = If(idade >= 18, "Adulto", "Menor")
Console.WriteLine(estado)  ' Adulto

' Coalescência (primeiro não-Nothing):
Dim nome As String = Nothing
Dim exibicao = If(nome, "Anónimo")
Console.WriteLine(exibicao)  ' Anónimo

A função If() com 3 argumentos age como um ternário. Com 2 argumentos, funciona como coalescência: devolve o primeiro valor que não seja Nothing.

Continuação de linha
' VB.NET moderno: quebra automática na maioria dos casos
Dim resultado = 1 + 2 +
                3 + 4

Dim lista = New List(Of String) From {
    "Ana",
    "Rui",
    "Mia"
}

' Antigo (VB9 e antes): usar _ (underscore)
Dim total = 1 + _
            2 + 3

Nas versões modernas a quebra de linha é automática em operadores e listas. Em código antigo usava-se o carácter de continuação _ (underscore) no fim da linha.

Operadores aritméticos
Dim a = 10, b = 3

Dim soma = a + b      ' 13
Dim dif = a - b       ' 7
Dim prod = a * b      ' 30
Dim divReal = a / b   ' 3.333... (Double)
Dim divInt = a \ b    ' 3 (divisão inteira)
Dim resto = a Mod b   ' 1
Dim pot = 2 ^ 10      ' 1024

a += 1   ' a = a + 1
a -= 1   ' a = a - 1

A barra / faz divisão real e a barra invertida \ faz divisão inteira. Mod dá o resto e ^ a potência.

Conversões de tipo
' Funções de conversão:
Dim n = CInt("42")          ' String -> Integer
Dim d = CDbl("3.14")        ' String -> Double
Dim s = CStr(42)            ' Integer -> String
Dim b = CBool(1)            ' -> Boolean
Dim dt = CDate("2024-01-15") ' -> Date

' Conversão segura (não lança exceção):
Dim resultado As Integer
Dim ok = Integer.TryParse("42", resultado)  ' True

' ToString:
Dim txt = 42.ToString()         ' "42"
Dim fmt = 3.14159.ToString("F2") ' "3.14"

As funções CInt, CDbl e CStr convertem tipos. Para evitar exceções em texto inválido, use TryParse, que devolve True ou False.

Múltiplas declarações
' Várias variáveis na mesma linha:
Dim x = 1, y = 2, z = 3

' Mesmo tipo explícito:
Dim a, b, c As Integer   ' só c é Integer!
' a e b ficam Object (cuidado)

' Forma segura:
Dim i As Integer = 0
Dim j As Integer = 0

' Atribuição composta:
x += 5    ' x = x + 5
y *= 2    ' y = y * 2

Atenção: em Dim a, b, c As Integerc fica Integer; os outros ficam Object. Prefira declarar cada variável com o seu tipo.

Funções e Procedimentos


12 cards
Function (com retorno)
Function Somar(a As Integer, b As Integer) As Integer
    Return a + b
End Function

' Chamar:
Dim r = Somar(3, 4)   ' 7

' Function com expressão única:
Function Dobro(n As Integer) As Integer
    Return n * 2
End Function

Uma Function devolve um valor com Return e declara o tipo de retorno com As. O nome da função descreve o que ela faz.

ByVal e ByRef
' ByVal (cópia - é o default):
Sub Incrementar(ByVal n As Integer)
    n += 1   ' não afeta o original
End Sub

' ByRef (referência - altera o original):
Sub Trocar(ByRef a As Integer, ByRef b As Integer)
    Dim temp = a
    a = b
    b = temp
End Sub

Dim x = 1, y = 2
Trocar(x, y)   ' x = 2, y = 1

ByVal passa uma cópia (o original não muda); ByRef passa a referência (o original é alterado). Em VB.NET o padrão é ByVal.

Métodos de extensão
Imports System.Runtime.CompilerServices

<Extension()>
Function Repetir(s As String, n As Integer) As String
    Return String.Concat(Enumerable.Repeat(s, n))
End Function

' Uso como se fosse método da String:
Dim r = "ab".Repetir(3)   ' "ababab"

Métodos de extensão adicionam métodos a tipos existentes sem os modificar. Precisam do atributo <Extension()> e de um Module; o 1º parâmetro é o tipo estendido.

Sub (sem retorno)
Sub Imprimir(msg As String)
    Console.WriteLine(msg)
End Sub

' Chamar:
Imprimir("Olá!")

' Sub pode terminar cedo com Exit Sub:
Sub Validar(n As Integer)
    If n < 0 Then Exit Sub
    Console.WriteLine(n)
End Sub

Uma Sub executa ações sem devolver valor. Use Exit Sub para terminar antes do fim. É ideal para procedimentos de impressão ou validação.

Funções lambda
' Lambda de uma expressão:
Dim dobro = Function(x As Integer) x * 2
Dim soma = Function(a As Integer, b As Integer) a + b

Console.WriteLine(dobro(5))   ' 10
Console.WriteLine(soma(3, 4)) ' 7

' Lambda Sub (sem retorno):
Dim imprimir = Sub(msg As String) Console.WriteLine(msg)
imprimir("Olá")

Uma lambda é uma função anónima curta. Function(...) devolve um valor; Sub(...) executa uma ação. São muito usadas com LINQ e eventos.

Propriedades
Private _nome As String

Public Property Nome As String
    Get
        Return _nome
    End Get
    Set(value As String)
        _nome = value.Trim()
    End Set
End Property

' Auto-property (sem campo manual):
Public Property Idade As Integer

' Só leitura:
Public ReadOnly Property NomeMaiusculo As String
    Get
        Return _nome.ToUpper()
    End Get
End Property

Uma Property expõe um campo com Get e Set. Auto-properties dispensam o campo privado. ReadOnly cria propriedades só de leitura.

Parâmetros opcionais e nomeados
Function Saudar(nome As String,
                Optional prefixo As String = "Olá") As String
    Return $"{prefixo}, {nome}!"
End Function

' Usar valor por defeito:
Console.WriteLine(Saudar("Ana"))         ' Olá, Ana!

' Argumento nomeado:
Console.WriteLine(Saudar("Rui", prefixo:="Bom dia"))

Parâmetros com Optional precisam de um valor por defeito. Os argumentos nomeados (prefixo:=) permitem saltar parâmetros opcionais com clareza.

Lambda multi-linha
Dim fatorial = Function(n As Integer)
                   If n <= 1 Then Return 1
                   Return n * fatorial(n - 1)
               End Function

Console.WriteLine(fatorial(5))  ' 120

Dim classificar = Function(n As Integer)
                      If n > 0 Then Return "positivo"
                      If n < 0 Then Return "negativo"
                      Return "zero"
                  End Function

Lambdas com várias linhas terminam com End Function (ou End Sub). O corpo pode ter If, ciclos e múltiplos Return.

Iteradores (Yield)
Function Contar(max As Integer) As IEnumerable(Of Integer)
    For i = 1 To max
        Yield i
    Next
End Function

' Consumo preguiçoso:
For Each n In Contar(5)
    Console.WriteLine(n)   ' 1 2 3 4 5
Next

O Yield devolve um elemento de cada vez, criando uma sequência preguiçosa. A função retorna IEnumerable(Of T) e só gera valores quando percorrida.

ParamArray (nº variável de argumentos)
Function SomarTudo(ParamArray nums() As Integer) As Integer
    Dim total = 0
    For Each n In nums
        total += n
    Next
    Return total
End Function

' Chamar com vários argumentos:
Console.WriteLine(SomarTudo(1, 2, 3))       ' 6
Console.WriteLine(SomarTudo(10, 20, 30, 40)) ' 100

ParamArray aceita um número variável de argumentos, tratados como um array dentro da função. Deve ser o último parâmetro da lista.

Funções locais (Local Functions)
Function Calcular(n As Integer) As Integer
    ' Função local, só visível aqui dentro:
    Function Quadrado(x As Integer) As Integer
        Return x * x
    End Function

    Return Quadrado(n) + Quadrado(n + 1)
End Function

Console.WriteLine(Calcular(3))  ' 9 + 16 = 25

Funções locais são declaradas dentro de outra Function e só existem nesse âmbito. Ajudam a organizar lógica auxiliar sem poluir a classe.

Funções recursivas
Function Fibonacci(n As Integer) As Long
    If n <= 1 Then Return n
    Return Fibonacci(n - 1) + Fibonacci(n - 2)
End Function

For i = 0 To 10
    Console.Write(Fibonacci(i) & " ")
Next
' 0 1 1 2 3 5 8 13 21 34 55

Uma função recursiva chama-se a si própria. Precisa sempre de um caso base (If n <= 1 Then Return n) para parar, senão entra em loop infinito.

Strings e Formatação


12 cards
Métodos de String
Dim s As String = "Hello World"

Console.WriteLine(s.Length)          ' 11
Console.WriteLine(s.ToUpper())       ' "HELLO WORLD"
Console.WriteLine(s.ToLower())       ' "hello world"
Console.WriteLine(s.Substring(0, 5)) ' "Hello"
Console.WriteLine(s.Contains("World")) ' True
Console.WriteLine(s.Replace("World", "VB"))
Console.WriteLine(s.Trim())          ' remove espaços
Console.WriteLine(s.IndexOf("World")) ' 6

As strings têm métodos como ToUpper, Substring, Contains e Replace. Strings são imutáveis: cada método devolve uma nova string.

Verbatim e escape
' Aspas duplas escapam-se duplicando:
Dim s = "Ele disse ""Olá"""

' Caminhos de ficheiro (sem escape de \):
Dim path = "C:\Users\Ana\docs"

' Nova linha e tabulação:
Dim texto = "Linha1" & vbCrLf & "Linha2" & vbTab & "fim"

' Constantes úteis:
Console.WriteLine(vbCrLf)    ' nova linha (Windows)
Console.WriteLine(vbTab)     ' tabulação
Console.WriteLine(vbNullString)  ' string vazia

Em VB.NET as aspas escapam-se duplicando (""), não com barra. vbCrLf e vbTab são constantes para nova linha e tabulação.

Trabalhar com datas
Dim hoje = Date.Now
Dim data = #2024-01-15#

Console.WriteLine(hoje.Year)
Console.WriteLine(hoje.Month)
Console.WriteLine(hoje.Day)
Console.WriteLine(hoje.DayOfWeek)

' Formatar:
Console.WriteLine(hoje.ToString("dd/MM/yyyy"))
Console.WriteLine(hoje.ToString("yyyy-MM-dd HH:mm"))

' Aritmética de datas:
Dim amanha = hoje.AddDays(1)
Dim diff = (amanha - hoje).TotalDays  ' 1

O tipo Date (ou DateTime) tem propriedades Year, Month, Day. Métodos como AddDays somam tempo e a subtração dá um TimeSpan.

Split e Join
Dim csv = "Ana,Rui,Mia"

' Separar por carácter:
Dim partes = csv.Split(","c)
For Each p In partes
    Console.WriteLine(p)
Next

' Separar por vários separadores:
Dim frase = "um dois;três"
Dim words = frase.Split({" "c, ";"c}, StringSplitOptions.RemoveEmptyEntries)

' Juntar:
Dim junto = String.Join(" | ", partes)  ' "Ana | Rui | Mia"

Split divide uma string num array por um separador (o sufixo c indica um Char). String.Join junta elementos com um delimitador.

StringBuilder
Imports System.Text

Dim sb As New StringBuilder()

For i = 1 To 5
    sb.Append("Linha ").AppendLine(i.ToString())
Next

sb.Insert(0, "=== Início ===" & vbCrLf)
sb.Replace("Linha", "Item")

Dim resultado = sb.ToString()
Console.WriteLine(resultado)

O StringBuilder é eficiente para construir strings em ciclos, pois não cria um novo objeto a cada alteração. Use Append, AppendLine e ToString.

Conversões de texto
Dim s = "123"

' Texto para número:
Dim n = Integer.Parse(s)
Dim d = Double.Parse("3.14")

' Número para texto:
Dim txt = n.ToString()
Dim hex = 255.ToString("X")    ' "FF"
Dim bin = Convert.ToString(5, 2)  ' "101"

' Caractere para código:
Dim code = Asc("A")        ' 65
Dim letra = Chr(65)        ' "A"

Parse converte texto em número (lança exceção se inválido). Asc dá o código de um carácter e Chr faz o inverso.

Interpolação de strings
Dim nome = "Ana"
Dim idade = 30

' Interpolação com $:
Console.WriteLine($"{nome} tem {idade} anos")
Console.WriteLine($"Em 5 anos: {idade + 5}")

' Com alinhamento e formato:
Console.WriteLine($"|{nome,-10}|{idade,5}|")

' String multi-linha:
Dim texto = $"Olá {nome},
bem-vinda!"

A interpolação com $ insere expressões dentro de {}. Pode alinhar com vírgula ({nome,-10}) e formatar com dois pontos.

Comparação de strings
Dim a = "Olá"
Dim b = "olá"

' Sensível a maiúsculas:
Console.WriteLine(a = b)              ' False

' Ignorando maiúsculas:
Console.WriteLine(String.Equals(a, b, StringComparison.OrdinalIgnoreCase))  ' True

Console.WriteLine(a.Equals(b, StringComparison.CurrentCultureIgnoreCase))

' Comparar ordem:
Console.WriteLine(String.Compare("abc", "abd"))  ' negativo

Por defeito a comparação distingue maiúsculas. Use StringComparison.OrdinalIgnoreCase para ignorar capitalização em Equals e Compare.

Verificar conteúdo de string
Dim s = "Olá Mundo"

Console.WriteLine(s.StartsWith("Olá"))   ' True
Console.WriteLine(s.EndsWith("Mundo"))   ' True
Console.WriteLine(s.Contains("Mun"))     ' True

' Vazia ou nula:
Dim x As String = ""
Console.WriteLine(String.IsNullOrEmpty(x))       ' True
Console.WriteLine(String.IsNullOrWhiteSpace("  ")) ' True

' Inverter:
Dim inv = StrReverse("abc")  ' "cba"

StartsWith, EndsWith e Contains verificam conteúdo. IsNullOrWhiteSpace é a verificação mais segura para entradas do utilizador.

String.Format
Dim nome = "Ana"
Dim idade = 30

' Format com posições:
Dim msg = String.Format("{0} tem {1} anos", nome, idade)

' Reutilizar a mesma posição:
Dim r = String.Format("{0} + {0} = {1}", 5, 10)  ' "5 + 5 = 10"

' Formatação numérica:
Console.WriteLine(String.Format("{0:F2}", 3.14159))  ' "3.14"
Console.WriteLine(String.Format("{0:D5}", 42))       ' "00042"
Console.WriteLine(String.Format("{0:X}", 255))       ' "FF"

String.Format substitui marcadores {0}, {1} pelos argumentos. Os códigos F2, D5 e X formatam números.

Pad, Trim e Remove
Dim s = "  Olá  "

Console.WriteLine(s.Trim())        ' "Olá"
Console.WriteLine(s.TrimStart())   ' "Olá  "
Console.WriteLine(s.TrimEnd())     ' "  Olá"

' Preencher até um tamanho:
Console.WriteLine("42".PadLeft(5, "0"c))   ' "00042"
Console.WriteLine("Hi".PadRight(6, "."c))  ' "Hi...."

' Remover parte:
Console.WriteLine("Olá Mundo".Remove(4))      ' "Olá "
Console.WriteLine("Olá Mundo".Remove(4, 3))   ' "Olá do"

Trim remove espaços nas pontas. PadLeft/PadRight preenchem até um tamanho. Remove apaga a partir de um índice.

Expressões regulares
Imports System.Text.RegularExpressions

Dim texto = "Contacto: 912345678 ou 923456789"

' Encontrar todas as correspondências:
Dim matches = Regex.Matches(texto, "\d{9}")
For Each m As Match In matches
    Console.WriteLine(m.Value)
Next

' Substituir:
Dim limpo = Regex.Replace(texto, "\d", "*")

' Validar:
Dim ok = Regex.IsMatch("ana@mail.com",
    "^[\w.]+@[\w.]+\.\w+$")

A classe Regex faz pesquisa avançada de padrões. Matches encontra todas as ocorrências, Replace substitui e IsMatch valida.

Classes e OOP


12 cards
Classe e construtor
Public Class Pessoa
    Public Property Nome As String
    Public Property Idade As Integer

    ' Construtor:
    Public Sub New(nome As String, idade As Integer)
        Me.Nome = nome
        Me.Idade = idade
    End Sub

    Public Function Saudacao() As String
        Return $"Olá, sou {Nome}"
    End Function
End Class

Dim p As New Pessoa("Ana", 30)

Uma classe define-se com Class...End Class. O construtor é o método Sub New e Me refere-se à instância atual.

NotInheritable e Shadows
' Classe selada (não pode ser herdada):
Public NotInheritable Class Utilitario
    Public Shared Function Versao() As String
        Return "1.0"
    End Function
End Class

' Shadows: esconde o membro da base:
Public Class Base
    Public Function Info() As String
        Return "Base"
    End Function
End Class

Public Class Filha
    Inherits Base
    Public Shadows Function Info() As String
        Return "Filha"
    End Function
End Class

NotInheritable impede a herança (classe selada). Shadows esconde um membro herdado com um novo de mesmo nome, sem polimorfismo.

Enum
Public Enum DiaSemana
    Segunda = 1
    Terca
    Quarta
    Quinta
    Sexta
    Sabado
    Domingo
End Enum

Dim dia = DiaSemana.Sexta
Console.WriteLine(dia)        ' Sexta
Console.WriteLine(CInt(dia))  ' 5

' Converter de número:
Dim d = CType(3, DiaSemana)   ' Quarta

Um Enum define um conjunto de constantes nomeadas. Os valores são inteiros por defeito (começando em 0 ou no valor indicado). CType converte números em enum.

Modificadores de acesso
Public Class Conta
    Public Saldo As Decimal        ' acessível em todo o lado
    Private _codigo As String      ' só dentro da classe
    Protected Titular As String    ' classe e derivadas
    Friend Banco As String         ' só no mesmo assembly

    Public Sub New()
        _codigo = GerarCodigo()    ' acede ao privado
    End Sub

    Private Function GerarCodigo() As String
        Return Guid.NewGuid().ToString()
    End Function
End Class

Public é acessível em todo o lado; Private só na classe; Protected na classe e derivadas; Friend apenas dentro do mesmo assembly.

Interfaces
Public Interface INadador
    Sub Nadar()
End Interface

Public Interface ICorredor
    Sub Correr()
End Interface

Public Class Atleta
    Implements INadador, ICorredor

    Public Sub Nadar() Implements INadador.Nadar
        Console.WriteLine("A nadar")
    End Sub

    Public Sub Correr() Implements ICorredor.Correr
        Console.WriteLine("A correr")
    End Sub
End Class

Uma Interface define um contrato sem implementação. A classe usa Implements para a adotar e liga cada método com Implements Nome.Membro.

Structure (tipo por valor)
Public Structure Ponto
    Public X As Double
    Public Y As Double

    Public Sub New(x As Double, y As Double)
        Me.X = x
        Me.Y = y
    End Sub

    Public Function Distancia() As Double
        Return Math.Sqrt(X ^ 2 + Y ^ 2)
    End Function
End Structure

Dim p As New Ponto(3, 4)
Console.WriteLine(p.Distancia())  ' 5

Uma Structure é um tipo por valor (guardado na stack), ideal para dados pequenos. Ao contrário das classes, não suporta herança e a cópia é feita por valor.

Herança
Public Class Animal
    Public Property Nome As String

    Public Overridable Function Som() As String
        Return "..."
    End Function
End Class

Public Class Cao
    Inherits Animal

    Public Overrides Function Som() As String
        Return "Au au!"
    End Function
End Class

Dim c As New Cao()
Console.WriteLine(c.Som())  ' Au au!

Inherits cria uma classe derivada. Overridable permite redefinir um método na base; Overrides fornece a nova implementação na derivada.

Shared (estático)
Public Class Contador
    Public Shared Total As Integer = 0

    Public Sub New()
        Total += 1
    End Sub

    Public Shared Function ObterTotal() As Integer
        Return Total
    End Function
End Class

Dim a As New Contador()
Dim b As New Contador()
Console.WriteLine(Contador.Total)  ' 2

Membros Shared pertencem à classe e não à instância. Acedem-se pelo nome da classe (Contador.Total) e são partilhados por todos os objetos.

Genéricos
' Classe genérica:
Public Class Caixa(Of T)
    Public Property Valor As T

    Public Sub New(valor As T)
        Me.Valor = valor
    End Sub
End Class

Dim c1 As New Caixa(Of Integer)(42)
Dim c2 As New Caixa(Of String)("olá")

Console.WriteLine(c1.Valor)  ' 42
Console.WriteLine(c2.Valor)  ' olá

Genéricos usam (Of T) para criar tipos reutilizáveis com qualquer tipo de dado. O mesmo código funciona para Integer, String, etc.

MustInherit e MustOverride
' Classe abstrata (não pode ser instanciada):
Public MustInherit Class Forma
    Public MustOverride Function Area() As Double

    Public Function Descrever() As String
        Return $"Área: {Area()}"
    End Function
End Class

Public Class Circulo
    Inherits Forma
    Public Raio As Double

    Public Overrides Function Area() As Double
        Return Math.PI * Raio ^ 2
    End Function
End Class

MustInherit torna a classe abstrata (não instanciável). MustOverride declara um método sem corpo que as derivadas são obrigadas a implementar.

Overrides e Overloads
Public Class Calculadora
    ' Overloads: mesmo nome, parâmetros diferentes
    Public Overloads Function Somar(a As Integer, b As Integer) As Integer
        Return a + b
    End Function

    Public Overloads Function Somar(a As Double, b As Double) As Double
        Return a + b
    End Function

    Public Overloads Function Somar(nums() As Integer) As Integer
        Return nums.Sum()
    End Function
End Class

Overloads permite vários métodos com o mesmo nome mas assinaturas diferentes (nº ou tipo de parâmetros). O compilador escolhe o certo pela chamada.

Restrições genéricas
' T deve ser uma classe (tipo por referência):
Public Class Repo(Of T As Class)
End Class

' T deve implementar uma interface:
Public Class Ordenador(Of T As IComparable(Of T))
    Public Function Max(a As T, b As T) As T
        Return If(a.CompareTo(b) >= 0, a, b)
    End Function
End Class

' T deve ter construtor sem parâmetros:
Public Class Fabrica(Of T As New)
    Public Function Criar() As T
        Return New T()
    End Function
End Class

Restrições (As Class, As New, As IComparable) limitam os tipos aceites por um genérico, permitindo usar métodos específicos com segurança.

Avançado


12 cards
Async / Await
Async Function DescarregarAsync(url As String) As Task(Of String)
    Using client As New HttpClient()
        Dim resultado = Await client.GetStringAsync(url)
        Return resultado
    End Using
End Function

' Chamar:
Async Function ExecutarAsync() As Task
    Dim dados = Await DescarregarAsync("https://api.exemplo.com")
    Console.WriteLine(dados)
End Function

Métodos Async devolvem Task ou Task(Of T). O Await pausa sem bloquear a thread, libertando a interface para outras tarefas.

Eventos
Public Class Termometro
    Public Event AoMudar(sender As Object, temp As Double)

    Private _temp As Double
    Public Property Temperatura As Double
        Get
            Return _temp
        End Get
        Set(value As Double)
            _temp = value
            RaiseEvent AoMudar(Me, value)
        End Set
    End Property
End Class

Um Event notifica subscritores quando algo acontece. RaiseEvent dispara o evento. É o padrão usado em interfaces gráficas e observadores.

Reflexão
Imports System.Reflection

Dim tipo = GetType(Pessoa)

Console.WriteLine(tipo.Name)        ' "Pessoa"
Console.WriteLine(tipo.FullName)

' Listar métodos:
For Each m As MethodInfo In tipo.GetMethods()
    Console.WriteLine(m.Name)
Next

' Listar propriedades:
For Each p In tipo.GetProperties()
    Console.WriteLine($"{p.Name}: {p.PropertyType.Name}")
Next

A reflexão (GetType, GetMethods, GetProperties) inspeciona tipos em tempo de execução. É usada por serializers e frameworks.

Task e Task.Run
' Executar trabalho em segundo plano:
Async Function ProcessarAsync() As Task(Of Integer)
    Dim resultado = Await Task.Run(Function()
                                       Threading.Thread.Sleep(1000)
                                       Return 42
                                   End Function)
    Return resultado
End Function

' Várias tarefas em paralelo:
Dim t1 = DescarregarAsync("url1")
Dim t2 = DescarregarAsync("url2")
Await Task.WhenAll(t1, t2)

Task.Run executa código numa thread do pool. Task.WhenAll aguarda várias tarefas em paralelo, melhorando o desempenho em operações independentes.

AddHandler e Handles
Dim t As New Termometro()

' Subscrever em tempo de execução:
AddHandler t.AoMudar,
    Sub(sender, temp) Console.WriteLine($"Temp: {temp}")

t.Temperatura = 36.5   ' imprime "Temp: 36.5"

' Ou com Handles (em classe):
Private Sub AoMudarHandler(sender As Object, temp As Double) _
    Handles t.AoMudar
    Console.WriteLine($"Mudou para {temp}")
End Sub

' Remover:
RemoveHandler t.AoMudar, AddressOf AoMudarHandler

AddHandler subscreve um evento em tempo de execução; Handles liga-o em tempo de compilação. RemoveHandler cancela a subscrição.

Atributos
<Obsolete("Use NovoMetodo em vez deste")>
Public Sub MetodoAntigo()
End Sub

' Atributo com parâmetros:
<DebuggerDisplay("Nome={Nome}, Idade={Idade}")>
Public Class Pessoa
    Public Property Nome As String
    Public Property Idade As Integer
End Class

' Atributo de assembly:
<Assembly: AssemblyVersion("1.0.0.0")>

Atributos (entre < >) adicionam metadados ao código. Obsolete marca código descontinuado; DebuggerDisplay personaliza a depuração.

Delegates
' Declarar um delegate:
Public Delegate Function Operacao(a As Integer, b As Integer) As Integer

Function Somar(a As Integer, b As Integer) As Integer
    Return a + b
End Function

' Usar:
Dim op As Operacao = AddressOf Somar
Dim r = op(3, 4)   ' 7

' Apontar para uma lambda:
Dim op2 As Operacao = Function(a, b) a * b
Console.WriteLine(op2(3, 4))  ' 12

Um Delegate é um tipo que referencia um método. AddressOf liga-o a um método existente. É a base de eventos e callbacks em VB.NET.

Nullable types
' Tipo por valor que pode ser Nothing:
Dim x As Integer? = Nothing

Console.WriteLine(x.HasValue)   ' False
Console.WriteLine(x.GetValueOrDefault())  ' 0

x = 42
Console.WriteLine(x.HasValue)   ' True
Console.WriteLine(x.Value)      ' 42

' Operador de coalescência:
Dim y As Integer? = Nothing
Dim resultado = If(y, 0)   ' 0 se y for Nothing

Um tipo Nullable (Integer?) pode conter Nothing além do valor normal. HasValue e Value verificam e obtêm o valor com segurança.

Operadores Nothing e Is
Dim s As String = Nothing

' Comparar com Nothing:
If s Is Nothing Then
    Console.WriteLine("sem valor")
End If

If s IsNot Nothing Then
    Console.WriteLine(s.Length)
End If

' Referência igual:
Dim a As New Object()
Dim b = a
Console.WriteLine(a Is b)        ' True (mesmo objeto)
Console.WriteLine(a IsNot b)     ' False

Use Is Nothing e IsNot Nothing para verificar referências nulas. Is compara identidade de objeto, não igualdade de valor.

Action e Func
' Func: devolve valor (último tipo é o retorno):
Dim dobro As Func(Of Integer, Integer) = Function(x) x * 2
Console.WriteLine(dobro(5))   ' 10

Dim soma As Func(Of Integer, Integer, Integer) =
    Function(a, b) a + b

' Action: não devolve valor:
Dim imprimir As Action(Of String) = Sub(msg) Console.WriteLine(msg)
imprimir("Olá")

' Predicate: devolve Boolean:
Dim par As Predicate(Of Integer) = Function(n) n Mod 2 = 0
Console.WriteLine(par(4))  ' True

Func e Action são delegates prontos: Func devolve um valor, Action não. Evitam declarar delegates personalizados para casos simples.

TypeOf e CType
Dim obj As Object = "Olá"

' Verificar o tipo:
If TypeOf obj Is String Then
    Console.WriteLine("É uma string")
End If

' Converter (lança exceção se falhar):
Dim s = CType(obj, String)

' Conversão segura (devolve Nothing se falhar):
Dim n = TryCast(obj, Integer)   ' Nothing
If n IsNot Nothing Then
    Console.WriteLine(n)
End If

TypeOf...Is verifica o tipo em tempo de execução. CType converte (lança exceção se impossível); TryCast devolve Nothing em vez de falhar.

NameOf e interpolação
Public Sub Validar(nome As String)
    If String.IsNullOrEmpty(nome) Then
        ' NameOf devolve o nome do parâmetro:
        Throw New ArgumentException("Inválido", NameOf(nome))
    End If
End Sub

Console.WriteLine(NameOf(Validar))   ' "Validar"
Console.WriteLine(NameOf(Pessoa.Nome))  ' "Nome"

NameOf devolve o nome de um símbolo como string, em tempo de compilação. É útil em mensagens de erro e notificações, evitando strings escritas à mão.

Controlo de Fluxo


12 cards
If / ElseIf / Else
Dim idade = 20

If idade >= 18 Then
    Console.WriteLine("Adulto")
ElseIf idade >= 12 Then
    Console.WriteLine("Adolescente")
Else
    Console.WriteLine("Criança")
End If

' Single-line (sem End If):
If idade > 0 Then Console.WriteLine("positivo")

O bloco If...Then...End If termina sempre com End If. Em instruções de uma linha o End If é opcional.

While e Do Loop
Dim x = 5

' While (verifica antes):
While x > 0
    x -= 1
End While

' Do Until (repete até ser verdadeiro):
x = 0
Do Until x = 10
    x += 1
Loop

' Do While (repete enquanto verdadeiro):
Do While x > 0
    x -= 1
Loop

While...End While repete enquanto a condição for verdadeira. Do Until repete até a condição ser verdadeira; Do While repete enquanto for verdadeira.

Throw e exceções personalizadas
Sub DefinirIdade(idade As Integer)
    If idade < 0 Then
        Throw New ArgumentException("Idade inválida")
    End If
End Sub

' Exceção personalizada:
Public Class SaldoInsuficienteException
    Inherits Exception

    Public Sub New(msg As String)
        MyBase.New(msg)
    End Sub
End Class

Throw New SaldoInsuficienteException("Sem fundos")

Throw lança uma exceção. Pode criar exceções próprias herdando de Exception e chamando MyBase.New para passar a mensagem.

Select Case
Dim dia = 3

Select Case dia
    Case 1
        Console.WriteLine("Segunda")
    Case 2, 3
        Console.WriteLine("Terça ou Quarta")
    Case 4 To 6
        Console.WriteLine("Meio da semana")
    Case Is >= 7
        Console.WriteLine("Fim de semana")
    Case Else
        Console.WriteLine("Inválido")
End Select

O Select Case é o switch do VB. Suporta valores únicos, listas (Case 2, 3), intervalos (Case 4 To 6) e condições com Is.

Do com condição no fim
Dim n = 0

' A condição é testada DEPOIS (executa pelo menos 1x):
Do
    n += 1
    Console.WriteLine(n)
Loop Until n >= 5

Do
    n -= 1
Loop While n > 0

Quando a condição vem depois do Loop, o bloco executa sempre pelo menos uma vez. É útil para menus e validações de entrada.

When (filtro de exceção)
Try
    Dim n = CInt(Console.ReadLine())
Catch ex As FormatException When ex.Message.Contains("input")
    Console.WriteLine("Formato específico")
Catch ex As Exception When TypeOf ex Is OverflowException
    Console.WriteLine("Valor demasiado grande")
Catch ex As Exception
    Console.WriteLine("Outro erro")
End Try

A cláusula When filtra exceções por uma condição booleana. Só o Catch cujo filtro devolver True é executado.

For ... Next
' Contagem crescente:
For i As Integer = 1 To 10
    Console.WriteLine(i)
Next

' Contagem decrescente com Step:
For i = 10 To 1 Step -1
    Console.WriteLine(i)
Next

' Passo de 2 em 2:
For i = 0 To 20 Step 2
    Console.WriteLine(i)
Next

O ciclo For...Next repete um bloco um número definido de vezes. O Step define o incremento (pode ser negativo para contar ao contrário).

Exit e Continue
For i = 1 To 10
    If i = 3 Then Continue For  ' salta para o próximo
    If i = 8 Then Exit For      ' sai do ciclo
    Console.WriteLine(i)
Next
' Imprime: 1 2 4 5 6 7

While True
    Dim cmd = Console.ReadLine()
    If cmd = "sair" Then Exit While
End While

Continue For salta para a próxima iteração; Exit For termina o ciclo imediatamente. Existem também Exit While, Exit Do e Exit Sub.

Using (recursos automáticos)
Imports System.IO

Using reader As New StreamReader("dados.txt")
    Dim linha = reader.ReadLine()
    Console.WriteLine(linha)
End Using
' reader é fechado/disposto automaticamente aqui

' Vários recursos:
Using fs = File.OpenRead("a.txt"),
      sr = New StreamReader(fs)
    Console.WriteLine(sr.ReadToEnd())
End Using

O bloco Using garante que objetos com recursos (ficheiros, ligações) são libertados automaticamente no fim, mesmo se ocorrer uma exceção.

For Each
Dim frutas = {"Maçã", "Pera", "Uva"}

For Each fruta In frutas
    Console.WriteLine(fruta)
Next

' Com índice (se precisar da posição):
For i = 0 To frutas.Length - 1
    Console.WriteLine($"{i}: {frutas(i)}")
Next

O For Each percorre cada elemento de uma coleção sem precisar de índice. Use o For normal quando necessitar da posição do elemento.

Try / Catch / Finally
Try
    Dim r = 10 \ 0
Catch ex As DivideByZeroException
    Console.WriteLine("Divisão por zero!")
Catch ex As Exception
    Console.WriteLine($"Erro: {ex.Message}")
Finally
    Console.WriteLine("Executa sempre")
End Try

O bloco Try protege código que pode falhar. O Catch captura a exceção (pode haver vários, do mais específico ao geral) e o Finally executa sempre.

With (acesso a membros)
Dim p As New Pessoa()

With p
    .Nome = "Ana"
    .Idade = 30
    .Email = "ana@mail.com"
End With

' Equivalente a:
' p.Nome = "Ana"
' p.Idade = 30
' p.Email = "ana@mail.com"

Console.WriteLine(p.Nome)

O bloco With evita repetir o nome do objeto ao definir várias propriedades. O ponto . refere-se ao objeto do With.

Coleções e LINQ


12 cards
Arrays
' Declarar e inicializar:
Dim nums() As Integer = {1, 2, 3, 4, 5}
Dim nomes = {"Ana", "Rui", "Mia"}

' Acesso (índice começa em 0):
Console.WriteLine(nums(0))   ' 1
nums(0) = 10

' Tamanho:
Console.WriteLine(nums.Length)  ' 5

Um array tem tamanho fixo e índice a começar em 0. Length devolve o número de elementos. Os valores são definidos entre chavetas {}.

List(Of T)
Dim lista As New List(Of String)

lista.Add("Ana")
lista.Add("Rui")
lista.Insert(0, "Mia")     ' insere no início
lista.Remove("Ana")        ' remove por valor
lista.RemoveAt(0)          ' remove por índice

Console.WriteLine(lista.Count)        ' 1
Console.WriteLine(lista.Contains("Rui"))  ' True
Console.WriteLine(lista(0))           ' Rui

A List(Of T) é um array dinâmico que cresce automaticamente. Tem Add, Insert, Remove e a propriedade Count.

HashSet(Of T)
Dim conjunto As New HashSet(Of Integer) From {1, 2, 3}

conjunto.Add(3)    ' ignorado (já existe)
conjunto.Add(4)

Console.WriteLine(conjunto.Count)       ' 4
Console.WriteLine(conjunto.Contains(2)) ' True

' Operações de conjuntos:
Dim a As New HashSet(Of Integer) From {1, 2, 3}
Dim b As New HashSet(Of Integer) From {2, 3, 4}
a.IntersectWith(b)   ' a = {2, 3}

O HashSet guarda valores únicos sem duplicados e com verificação rápida de existência. Tem operações como IntersectWith, UnionWith e ExceptWith.

ReDim e Preserve
Dim nums(4) As Integer   ' 5 elementos (0 a 4)

' Redimensionar (perde os dados):
ReDim nums(9)

' Redimensionar mantendo os dados:
Dim lista(2) As String
lista(0) = "A"
lista(1) = "B"
ReDim Preserve lista(5)   ' mantém A e B

Console.WriteLine(lista.Length)  ' 6

ReDim muda o tamanho do array. Sem Preserve os dados perdem-se; com Preserve os elementos existentes são mantidos.

List - pesquisa e ordenação
Dim nums As New List(Of Integer) From {5, 2, 8, 1, 9}

nums.Sort()                    ' ordena
nums.Reverse()                 ' inverte

' Encontrar com predicado:
Dim maior5 = nums.Find(Function(n) n > 5)
Dim todos = nums.FindAll(Function(n) n Mod 2 = 0)

' Remover com condição:
nums.RemoveAll(Function(n) n < 3)

Dim existe = nums.Exists(Function(n) n = 8)

Find devolve o primeiro elemento que satisfaz o predicado; FindAll devolve todos. RemoveAll e Exists também recebem uma lambda.

LINQ - sintaxe de consulta
Dim nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

Dim pares = From n In nums
            Where n Mod 2 = 0
            Order By n Descending
            Select n * 2

For Each r In pares
    Console.WriteLine(r)
Next

A sintaxe de consulta LINQ parece SQL: From define a fonte, Where filtra, Order By ordena e Select projeta o resultado.

Operações com arrays
Dim nums = {5, 2, 8, 1, 9}

Array.Sort(nums)        ' ordena: 1 2 5 8 9
Array.Reverse(nums)     ' inverte: 9 8 5 2 1

Dim pos = Array.IndexOf(nums, 5)   ' posição de 5
Dim existe = nums.Contains(8)      ' True

Dim total = nums.Sum()
Dim maior = nums.Max()
Dim media = nums.Average()

A classe Array oferece Sort, Reverse e IndexOf. Os métodos de extensão LINQ como Sum, Max e Average fazem cálculos rápidos.

Dictionary(Of TKey, TValue)
Dim idades As New Dictionary(Of String, Integer)

idades("Ana") = 30
idades.Add("Rui", 25)
idades("Mia") = 22

Console.WriteLine(idades("Ana"))         ' 30
Console.WriteLine(idades.ContainsKey("Rui"))  ' True
Console.WriteLine(idades.Count)          ' 3

' Acesso seguro:
Dim valor As Integer
If idades.TryGetValue("Zé", valor) Then
    Console.WriteLine(valor)
End If

O Dictionary guarda pares chave/valor com acesso rápido pela chave. TryGetValue evita exceção quando a chave não existe.

LINQ - sintaxe de método
Dim nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

Dim r = nums.Where(Function(n) n > 5) _
            .OrderByDescending(Function(n) n) _
            .Take(3) _
            .ToList()

' Outros métodos úteis:
Dim soma = nums.Sum()
Dim pares = nums.Count(Function(n) n Mod 2 = 0)
Dim primeiro = nums.First(Function(n) n > 3)

A sintaxe de método encadeia operadores como Where, OrderBy e Take usando lambdas. Sum, Count e First fazem agregações.

Arrays multidimensionais
' Matriz 2D (3 linhas x 3 colunas):
Dim matriz(,) As Integer = {
    {1, 2, 3},
    {4, 5, 6},
    {7, 8, 9}
}

Console.WriteLine(matriz(1, 2))  ' 6

' Percorrer:
For i = 0 To matriz.GetLength(0) - 1
    For j = 0 To matriz.GetLength(1) - 1
        Console.Write(matriz(i, j) & " ")
    Next
    Console.WriteLine()
Next

Arrays multidimensionais usam vírgula na declaração: (,) para 2D. GetLength(0) dá o nº de linhas e GetLength(1) o nº de colunas.

Percorrer Dictionary
Dim idades As New Dictionary(Of String, Integer) From {
    {"Ana", 30},
    {"Rui", 25},
    {"Mia", 22}
}

For Each par In idades
    Console.WriteLine($"{par.Key}: {par.Value}")
Next

' Só as chaves / só os valores:
For Each nome In idades.Keys
    Console.WriteLine(nome)
Next

Ao percorrer um Dictionary cada elemento é um par com Key e Value. As propriedades Keys e Values dão acesso separado.

LINQ - projeção e agrupamento
Dim pessoas = GetPessoas()

' Projeção (Select):
Dim nomes = pessoas.Select(Function(p) p.Nome).ToList()

' Agrupamento:
Dim porCidade = From p In pessoas
                Group By p.Cidade Into Grupo = Group
                Select Cidade, Total = Grupo.Count()

' Distintos:
Dim cidades = pessoas.Select(Function(p) p.Cidade).Distinct()

Select transforma cada elemento. Group By...Into Group agrupa por uma chave e Distinct remove duplicados do resultado.

Entrada/Saída e Ficheiros


10 cards
Entrada e saída na consola
' Saída:
Console.WriteLine("Com nova linha")
Console.Write("Sem nova linha")
Console.WriteLine($"Valor: {42}")

' Entrada:
Console.Write("Nome: ")
Dim nome = Console.ReadLine()

' Ler um número:
Console.Write("Idade: ")
Dim idade = CInt(Console.ReadLine())

' Limpar e cores:
Console.ForegroundColor = ConsoleColor.Green
Console.WriteLine("Sucesso!")
Console.ResetColor()

Console.WriteLine escreve com nova linha; ReadLine lê uma linha como string. ForegroundColor muda a cor do texto na consola.

Caminhos (Path)
Imports System.IO

Dim caminho = "C:\dados\relatorio.txt"

Console.WriteLine(Path.GetFileName(caminho))    ' "relatorio.txt"
Console.WriteLine(Path.GetExtension(caminho))   ' ".txt"
Console.WriteLine(Path.GetFileNameWithoutExtension(caminho))
Console.WriteLine(Path.GetDirectoryName(caminho))

' Juntar caminhos com segurança:
Dim novo = Path.Combine("C:\dados", "sub", "f.txt")

' Ficheiro temporário:
Dim temp = Path.GetTempFileName()

A classe Path manipula caminhos sem erros de separadores. Path.Combine junta partes com segurança e GetExtension obtém a extensão.

Aceder a base de dados
Imports System.Data.SqlClient

Dim connStr = "Server=.;Database=Loja;Trusted_Connection=True;"

Using conn As New SqlConnection(connStr)
    conn.Open()
    Using cmd As New SqlCommand("SELECT Nome FROM Cliente", conn)
        Using reader = cmd.ExecuteReader()
            While reader.Read()
                Console.WriteLine(reader("Nome"))
            End While
        End Using
    End Using
End Using

Acesso ADO.NET: SqlConnection liga à base de dados, SqlCommand executa a query e ExecuteReader percorre os resultados. Use sempre Using.

Escrever ficheiros
Imports System.IO

' Escrever tudo de uma vez:
File.WriteAllText("f.txt", "Olá Mundo")

' Várias linhas:
File.WriteAllLines("f.txt", {"L1", "L2", "L3"})

' Acrescentar ao fim:
File.AppendAllText("log.txt", "nova linha" & vbCrLf)

' Com StreamWriter (mais controlo):
Using sw As New StreamWriter("f.txt")
    sw.WriteLine("Linha 1")
    sw.WriteLine("Linha 2")
End Using

File.WriteAllText e WriteAllLines criam/sobrescrevem ficheiros. AppendAllText acrescenta. O StreamWriter dá mais controlo em escrita progressiva.

Listar ficheiros e pastas
Imports System.IO

' Listar ficheiros:
For Each f In Directory.GetFiles("C:\dados")
    Console.WriteLine(f)
Next

' Com filtro e recursão:
Dim txts = Directory.GetFiles("C:\dados", "*.txt",
    SearchOption.AllDirectories)

' Listar pastas:
For Each d In Directory.GetDirectories("C:\dados")
    Console.WriteLine(d)
Next

' Versão lazy (memória eficiente):
For Each f In Directory.EnumerateFiles("C:\dados", "*.log")
    Console.WriteLine(f)
Next

GetFiles e GetDirectories listam conteúdos. O filtro *.txt seleciona por extensão e SearchOption.AllDirectories pesquisa recursivamente.

Parâmetros SQL (anti-injeção)
Using conn As New SqlConnection(connStr)
    conn.Open()
    Dim sql = "SELECT * FROM Cliente WHERE Idade > @idade"
    Using cmd As New SqlCommand(sql, conn)
        cmd.Parameters.AddWithValue("@idade", 18)

        Dim count = CInt(cmd.ExecuteScalar())
        Console.WriteLine($"Maiores: {count}")
    End Using
End Using

' Inserir com parâmetros:
Dim ins = "INSERT INTO Cliente (Nome) VALUES (@nome)"

Use sempre parâmetros (@idade) em vez de concatenar strings nas queries. Isto previne injeção de SQL. ExecuteScalar devolve um único valor.

Ler ficheiros
Imports System.IO

' Ler tudo como texto:
Dim conteudo = File.ReadAllText("f.txt")

' Ler como array de linhas:
Dim linhas = File.ReadAllLines("f.txt")
For Each linha In linhas
    Console.WriteLine(linha)
Next

' Com StreamReader (linha a linha):
Using sr As New StreamReader("f.txt")
    Dim l As String
    While (InlineAssignHelper(l, sr.ReadLine())) IsNot Nothing
        Console.WriteLine(l)
    End While
End Using

ReadAllText devolve o ficheiro inteiro; ReadAllLines devolve um array de linhas. O StreamReader é eficiente para ficheiros grandes, lendo linha a linha.

Serialização JSON
Imports System.Text.Json

Public Class Pessoa
    Public Property Nome As String
    Public Property Idade As Integer
End Class

' Serializar:
Dim p As New Pessoa With {.Nome = "Ana", .Idade = 30}
Dim json = JsonSerializer.Serialize(p)

' Desserializar:
Dim p2 = JsonSerializer.Deserialize(Of Pessoa)(json)
Console.WriteLine(p2.Nome)  ' Ana

JsonSerializer.Serialize converte um objeto em JSON; Deserialize(Of T) faz o inverso. Está incluído em System.Text.Json no .NET moderno.

Verificar e gerir ficheiros
Imports System.IO

' Verificar existência:
If File.Exists("f.txt") Then
    Console.WriteLine("Existe")
End If

If Directory.Exists("pasta") Then
    Console.WriteLine("Pasta existe")
End If

' Criar pasta:
Directory.CreateDirectory("dados/logs")

' Apagar:
File.Delete("temp.txt")

' Mover / copiar:
File.Copy("a.txt", "b.txt", overwrite:=True)
File.Move("a.txt", "c.txt")

File.Exists e Directory.Exists verificam existência. CreateDirectory cria pastas; Delete, Copy e Move gerem ficheiros.

Serialização XML
Imports System.Xml.Serialization

<Serializable()>
Public Class Produto
    Public Property Nome As String
    Public Property Preco As Decimal
End Class

Dim serializer As New XmlSerializer(GetType(Produto))

Using sw As New StreamWriter("produto.xml")
    serializer.Serialize(sw, New Produto With {.Nome = "Livro", .Preco = 9.9})
End Using

O XmlSerializer converte objetos em XML e vice-versa. O tipo deve ser serializável e ter um construtor sem parâmetros. Use Serialize e Deserialize.

Tipos e Recursos Extra


10 cards
Tipos anónimos
Dim pessoa = New With {
    .Nome = "Ana",
    .Idade = 30
}

Console.WriteLine(pessoa.Nome)   ' Ana
Console.WriteLine(pessoa.Idade)  ' 30

' Em LINQ:
Dim resultados = From n In {1, 2, 3, 4}
                 Select New With {.Numero = n, .Dobro = n * 2}

Tipos anónimos (New With { }) criam objetos sem definir uma classe. São úteis em projeções LINQ para resultados temporários com propriedades nomeadas.

Math (matemática)
Console.WriteLine(Math.PI)            ' 3.14159...
Console.WriteLine(Math.E)

Console.WriteLine(Math.Abs(-5))       ' 5
Console.WriteLine(Math.Max(3, 7))     ' 7
Console.WriteLine(Math.Min(3, 7))     ' 3
Console.WriteLine(Math.Pow(2, 10))    ' 1024
Console.WriteLine(Math.Sqrt(16))      ' 4
Console.WriteLine(Math.Round(3.7))    ' 4
Console.WriteLine(Math.Floor(3.7))    ' 3
Console.WriteLine(Math.Ceiling(3.2))  ' 4

A classe Math tem constantes (PI, E) e funções como Abs, Pow, Sqrt, Round, Floor e Ceiling.

Coleções especializadas
Imports System.Collections.Generic

' Queue (FIFO - primeiro a entrar, primeiro a sair):
Dim fila As New Queue(Of String)
fila.Enqueue("A")
fila.Enqueue("B")
Console.WriteLine(fila.Dequeue())  ' A

' Stack (LIFO - último a entrar, primeiro a sair):
Dim pilha As New Stack(Of Integer)
pilha.Push(1)
pilha.Push(2)
Console.WriteLine(pilha.Pop())  ' 2

Queue é uma fila (FIFO) com Enqueue/Dequeue. Stack é uma pilha (LIFO) com Push/Pop. Escolha conforme a ordem necessária.

Tuplos
' Tuplo com nomes:
Dim pessoa = (Nome:="Ana", Idade:=30)
Console.WriteLine(pessoa.Nome)   ' Ana
Console.WriteLine(pessoa.Idade)  ' 30

' Retornar vários valores de uma função:
Function Dividir(a As Integer, b As Integer) As (Quociente As Integer, Resto As Integer)
    Return (a \ b, a Mod b)
End Function

Dim r = Dividir(10, 3)
Console.WriteLine($"{r.Quociente} resto {r.Resto}")  ' 3 resto 1

Tuplos agrupam vários valores sem criar uma classe. Com nomes (Nome:=) os campos ficam legíveis. São ideais para devolver múltiplos valores de uma função.

Random (aleatórios)
Dim rnd As New Random()

' Inteiro aleatório:
Dim n = rnd.Next()              ' 0 até Integer.MaxValue
Dim dado = rnd.Next(1, 7)       ' 1 a 6

' Double entre 0.0 e 1.0:
Dim f = rnd.NextDouble()

' Preencher array de bytes:
Dim bytes(15) As Byte
rnd.NextBytes(bytes)

' Partilhar uma instância (evita repetição):
Shared ReadOnly Rnd As New Random()

A classe Random gera números aleatórios. Next(min, max) devolve um inteiro no intervalo (máx exclusivo). Reutilize a instância para evitar valores repetidos.

Conversões de coleção
Dim lista As New List(Of Integer) From {1, 2, 3}

' List para Array:
Dim arr = lista.ToArray()

' Array para List:
Dim lista2 = arr.ToList()

' List para Dictionary:
Dim pessoas = GetPessoas()
Dim dict = pessoas.ToDictionary(
    Function(p) p.Id,
    Function(p) p.Nome)

' Filtrar para nova lista:
Dim pares = lista.Where(Function(n) n Mod 2 = 0).ToList()

ToArray e ToList convertem entre coleções. ToDictionary cria um dicionário a partir de uma lista usando funções para chave e valor.

Coleções imutáveis
Imports System.Collections.Immutable

Dim lista = ImmutableList.CreateRange({1, 2, 3})

' "Alterar" devolve uma nova lista:
Dim lista2 = lista.Add(4)

Console.WriteLine(lista.Count)   ' 3 (inalterada)
Console.WriteLine(lista2.Count)  ' 4

Dim dict = ImmutableDictionary(Of String, Integer).Empty
Dim dict2 = dict.Add("Ana", 30)

Coleções imutáveis (ImmutableList, ImmutableDictionary) nunca mudam: cada operação devolve uma nova coleção. São seguras em código concorrente.

Guid (identificador único)
' Gerar um GUID:
Dim id = Guid.NewGuid()
Console.WriteLine(id)   ' ex: 3f2a...-...-...

' Como string sem hífens:
Dim s = id.ToString("N")

' GUID vazio:
Dim vazio = Guid.Empty
Console.WriteLine(id = Guid.Empty)  ' False

' Converter de string:
Dim g = Guid.Parse("3f2a1b...")

Guid.NewGuid() gera um identificador único global de 128 bits. É usado como chave primária e para nomes únicos. Guid.Empty é o valor nulo.

Conversões avançadas
' Enum para string e vice-versa:
Dim dia = DiaSemana.Sexta
Dim nome = dia.ToString()             ' "Sexta"
Dim volta = [Enum].Parse(GetType(DiaSemana), "Sexta")

' Byte array para Base64:
Dim bytes = {1, 2, 3, 4}
Dim b64 = Convert.ToBase64String(bytes)
Dim orig = Convert.FromBase64String(b64)

' Objeto para string:
Dim o As Object = 42
Dim s = Convert.ToString(o)

Enum.Parse converte texto em enum. Convert.ToBase64String codifica bytes em Base64. A classe Convert faz conversões entre tipos diversos.

TimeSpan
' Criar um intervalo de tempo:
Dim duracao = New TimeSpan(1, 30, 0)   ' 1h 30m
Console.WriteLine(duracao.TotalMinutes)  ' 90

' Métodos de fábrica:
Dim t = TimeSpan.FromHours(2)
Dim t2 = TimeSpan.FromSeconds(45)

' Diferença entre datas:
Dim inicio = #2024-01-01#
Dim fim = #2024-03-15#
Dim dias = (fim - inicio).TotalDays
Console.WriteLine($"{dias} dias")

TimeSpan representa uma duração. TotalMinutes e TotalHours convertem para unidades. A diferença entre duas datas devolve um TimeSpan.

Dicas e Boas Práticas


8 cards
Inicializadores de objeto
' Sem construtor, definindo propriedades:
Dim p As New Pessoa With {
    .Nome = "Ana",
    .Idade = 30
}

' Coleção com inicializador:
Dim pessoas As New List(Of Pessoa) From {
    New Pessoa With {.Nome = "Rui", .Idade = 25},
    New Pessoa With {.Nome = "Mia", .Idade = 22}
}

Console.WriteLine(pessoas.Count)  ' 2

O bloco With { } inicializa propriedades ao criar o objeto, sem precisar de construtor. Combina com From { } para preencher coleções de forma legível.

Tratamento defensivo
Sub Processar(lista As List(Of String))
    ' Validar argumentos cedo:
    If lista Is Nothing Then
        Throw New ArgumentNullException(NameOf(lista))
    End If

    If lista.Count = 0 Then Return

    For Each item In lista
        If String.IsNullOrWhiteSpace(item) Then Continue For
        Console.WriteLine(item.Trim())
    Next
End Sub

Valide argumentos no início com ArgumentNullException. Use Return e Continue For para sair cedo de casos inválidos, mantendo o código principal sem ninhos.

Operador de coalescência e Elvis
Dim nome As String = Nothing

' If() com 2 args: primeiro não-Nothing:
Dim exibicao = If(nome, "Anónimo")

' Acesso seguro com ?.:
Dim comprimento = nome?.Length   ' Nothing (sem erro)

' Combinação útil:
Dim tam = nome?.Length ?? -1
' Em VB usa-se If aninhado:
Dim tam2 = If(nome IsNot Nothing, nome.Length, -1)

O operador ?. (null-conditional) só acede ao membro se o objeto não for Nothing, evitando NullReferenceException. If(a, b) faz coalescência.

Padrão Repository (exemplo)
Public Interface IRepositorio(Of T)
    Function Obter(id As Integer) As T
    Function Todos() As List(Of T)
    Sub Guardar(entidade As T)
    Sub Remover(id As Integer)
End Interface

Public Class ClienteRepositorio
    Implements IRepositorio(Of Cliente)

    Public Function Obter(id As Integer) As Cliente _
        Implements IRepositorio(Of Cliente).Obter
        ' aceder à BD...
        Return New Cliente()
    End Function
End Class

O padrão Repository abstrai o acesso a dados atrás de uma interface genérica IRepositorio(Of T). Facilita testes e a troca da fonte de dados sem mudar a lógica.

Padrão Dispose e IDisposable
Public Class Ligacao
    Implements IDisposable

    Private _aberta As Boolean = True

    Public Sub Fechar()
        If _aberta Then
            _aberta = False
            Console.WriteLine("Fechada")
        End If
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        Fechar()
        GC.SuppressFinalize(Me)
    End Sub
End Class

' Uso com Using:
Using l As New Ligacao()
    ' ...
End Using

Classes com recursos implementam IDisposable com o método Dispose. O Using chama Dispose automaticamente, garantindo a libertação.

Padrão Singleton
Public Class Configuracao
    ' Instância única, criada de forma thread-safe:
    Private Shared ReadOnly _instancia As New Lazy(Of Configuracao)(
        Function() New Configuracao())

    Public Shared ReadOnly Property Instancia As Configuracao
        Get
            Return _instancia.Value
        End Get
    End Property

    Private Sub New()   ' construtor privado
    End Sub

    Public Property Tema As String = "Claro"
End Class

Dim cfg = Configuracao.Instancia

O Singleton garante uma única instância. O construtor Private impede criação externa e Lazy(Of T) cria a instância só quando necessária, de forma thread-safe.

Convenções de nomenclatura
' Classes, métodos, propriedades: PascalCase
Public Class ClienteFidelizado
    Public Property NomeCompleto As String

    Public Function CalcularDesconto() As Decimal
        Return 0
    End Function
End Class

' Variáveis locais e parâmetros: camelCase
Dim totalCompra As Decimal
Sub Processar(valor As Decimal)
End Sub

' Campos privados: _camelCase
Private _saldo As Decimal

Use PascalCase para classes, métodos e propriedades; camelCase para variáveis locais e parâmetros; prefixo _ para campos privados. Interfaces começam por I.

Boas práticas gerais
' 1. Option Strict On sempre
Option Strict On

' 2. Usar Using para recursos
Using conn = AbrirLigacao()
    ' ...
End Using

' 3. Preferir coleções genéricas
Dim lista As New List(Of Integer)  ' não ArrayList

' 4. Async em vez de bloquear
Dim dados = Await ObterAsync()     ' não .Result

' 5. Exceções específicas
Try
    ' ...
Catch ex As IOException
    ' tratar
End Try

Ative Option Strict On, use Using para recursos, prefira coleções genéricas, use Await em vez de bloquear e capture exceções específicas.