DevTools

Cheatsheet Visual Basic

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

Volver a los lenguajes
Visual Basic
112 tarjetas encontradas
Categorías:
Versiones:

Básico e Estrutura


12 cards
Estructura del programa
Imports System

Module Program
    Sub Main(args As String())
        Console.WriteLine("¡Hola Mundo!")
        Console.Write("Nombre: ")
        Dim nombre = Console.ReadLine()
        Console.WriteLine($"¡Hola, {nombre}!")
    End Sub
End Module

Un programa de consola VB.NET comienza en un Module con el método Sub Main. El Imports trae namespaces y el Console escribe en pantalla.

Operadores de comparación y lógicos
Dim x = 10

' Comparación (devuelven Boolean):
Dim r1 = x = 10     ' True
Dim r2 = x <> 5     ' True (distinto)
Dim r3 = x >= 10    ' True

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

' Cortocircuito (recomendado):
If x > 0 AndAlso x < 100 Then
    Console.WriteLine("entre 0 y 100")
End If

AndAlso y OrElse son las versiones de cortocircuito: dejan de evaluar en cuanto el resultado se conoce, evitando errores y ganando rendimiento.

Option Strict y Option Infer
' En la parte superior del archivo:
Option Strict On    ' prohíbe conversiones implícitas
Option Infer On     ' permite inferir tipos
Option Explicit On  ' obliga a declarar variables

' Con Option Strict On, esto da error:
' Dim n As Integer = "42"   ' ¡error!

' Hay que convertir explícitamente:
Dim n As Integer = CInt("42")

' Inferencia:
Dim lista = New List(Of String)()  ' tipo inferido

Option Strict On es una buena práctica: impide conversiones automáticas que esconden bugs. Option Infer On deja al compilador deducir el tipo a partir del valor.

Variables con Dim
Dim nombre As String = "VB.NET"
Dim edad As Integer = 30
Dim activo As Boolean = True
Dim pi As Double = 3.14
Dim precio As Decimal = 99.99D

' Inferencia de tipo (Option Infer On):
Dim x = 42        ' Integer
Dim s = "Hola"    ' String

Const MAX As Integer = 100

Las variables se declaran con Dim. El tipo puede ser explícito (As Integer) o inferido por el valor. Const crea constantes inmutables.

Concatenación de strings
Dim nombre = "Ana"
Dim apellido = "Silva"

' Operador & (concatenación):
Dim completo = nombre & " " & apellido

' Operador + (también funciona):
Dim s = "Hola " + nombre

' Juntar con números:
Dim edad = 30
Dim msg = nombre & " tiene " & edad & " años"

Console.WriteLine(completo)  ' Ana Silva

El operador & es el recomendado para concatenar, pues convierte automáticamente números en texto. El + puede causar ambigüedad con la suma.

Comentarios
' Esto es un comentario de línea

Dim x = 10  ' comentario al final de la línea

REM También funciona (antiguo, poco usado)

''' <summary>
''' Comentario de documentación XML
''' </summary>
''' <param name="n">El número</param>
Function Doble(n As Integer) As Integer
    Return n * 2
End Function

El despuéstrofo &después; inicia un comentario. Los comentarios XML con &después;&después;&después; generan documentación IntelliSense para los métodos.

Tipos de datos
' Enteros: Byte, Short, Integer, Long
' Decimales: Single, Double, Decimal
' Otros:  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 = "cualquier cosa"

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

VB.NET usa los tipos de .NET. Los literales de fecha van entre #, y sufijos como L, F y D indican el tipo numérico.

Operador ternario If()
Dim edad = 20

' If(condición, valorSiVerdadero, valorSiFalso)
Dim estado = If(edad >= 18, "Adulto", "Menor")
Console.WriteLine(estado)  ' Adulto

' Coalescencia (primero no-Nothing):
Dim nombre As String = Nothing
Dim mostrado = If(nombre, "Anónimo")
Console.WriteLine(mostrado)  ' Anónimo

La función If() con 3 argumentos actúa como un ternario. Con 2 argumentos, funciona como coalescencia: devuelve el primer valor que no sea Nothing.

Continuación de línea
' VB.NET moderno: salto automático en la mayoría de los casos
Dim resultado = 1 + 2 +
                3 + 4

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

' Antiguo (VB9 y anteriores): usar _ (guion bajo)
Dim total = 1 + _
            2 + 3

En las versiones modernas el salto de línea es automático en operadores y listas. En código antiguo se usaba el carácter de continuación _ (guion bajo) al final de la línea.

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

Dim suma = a + b      ' 13
Dim dif = a - b       ' 7
Dim prod = a * b      ' 30
Dim divReal = a / b   ' 3.333... (Double)
Dim divEnt = a \ b    ' 3 (división entera)
Dim resto = a Mod b   ' 1
Dim pot = 2 ^ 10      ' 1024

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

La barra / hace división real y la barra invertida \ hace división entera. Mod da el resto y ^ la potencia.

Conversiones de tipo
' Funciones de conversión:
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

' Conversión segura (no lanza excepción):
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"

Las funciones CInt, CDbl y CStr convierten tipos. Para evitar excepciones con texto inválido, usa TryParse, que devuelve True o False.

Múltiples declaraciones
' Varias variables en la misma línea:
Dim x = 1, y = 2, z = 3

' Mismo tipo explícito:
Dim a, b, c As Integer   ' ¡solo c es Integer!
' a y b quedan Object (cuidado)

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

' Asignación compuesta:
x += 5    ' x = x + 5
y *= 2    ' y = y * 2

Atención: en Dim a, b, c As Integer solo c queda Integer; los demás quedan Object. Prefiere declarar cada variable con su tipo.

Funções e Procedimentos


12 cards
Function (con retorno)
Function Sumar(a As Integer, b As Integer) As Integer
    Return a + b
End Function

' Llamar:
Dim r = Sumar(3, 4)   ' 7

' Function de expresión única:
Function Doble(n As Integer) As Integer
    Return n * 2
End Function

Una Function devuelve un valor con Return y declara el tipo de retorno con As. El nombre de la función describe lo que hace.

ByVal y ByRef
' ByVal (copia - es el default):
Sub Incrementar(ByVal n As Integer)
    n += 1   ' no afecta al original
End Sub

' ByRef (referencia - cambia el original):
Sub Intercambiar(ByRef a As Integer, ByRef b As Integer)
    Dim temp = a
    a = b
    b = temp
End Sub

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

ByVal pasa una copia (el original no cambia); ByRef pasa la referencia (el original se modifica). En VB.NET el estándar es ByVal.

Métodos de extensión
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 si fuera método de String:
Dim r = "ab".Repetir(3)   ' "ababab"

Los métodos de extensión añaden métodos a tipos existentes sin modificarlos. Necesitan el atributo <Extension()> y un Module; el 1er parámetro es el tipo extendido.

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

' Llamar:
Imprimir("¡Hola!")

' Sub puede terminar antes con Exit Sub:
Sub Validar(n As Integer)
    If n < 0 Then Exit Sub
    Console.WriteLine(n)
End Sub

Una Sub ejecuta acciones sin devolver valor. Usa Exit Sub para terminar antes del final. Es ideal para procedimientos de impresión o validación.

Funciones lambda
' Lambda de una expresión:
Dim doble = Function(x As Integer) x * 2
Dim suma = Function(a As Integer, b As Integer) a + b

Console.WriteLine(doble(5))   ' 10
Console.WriteLine(suma(3, 4)) ' 7

' Lambda Sub (sin retorno):
Dim imprimir = Sub(msg As String) Console.WriteLine(msg)
imprimir("Hola")

Una lambda es una función anónima corta. Function(...) devuelve un valor; Sub(...) ejecuta una acción. Se usan mucho con LINQ y eventos.

Propiedades
Private _nombre As String

Public Property Nombre As String
    Get
        Return _nombre
    End Get
    Set(value As String)
        _nombre = value.Trim()
    End Set
End Property

' Auto-property (sin campo manual):
Public Property Edad As Integer

' Solo lectura:
Public ReadOnly Property NombreMayusculas As String
    Get
        Return _nombre.ToUpper()
    End Get
End Property

Una Property expone un campo con Get y Set. Las auto-properties evitan el campo privado. ReadOnly crea propiedades de solo lectura.

Parámetros opcionales y con nombre
Function Saludar(nombre As String,
                 Optional prefijo As String = "Hola") As String
    Return $"{prefijo}, {nombre}!"
End Function

' Usar valor por defecto:
Console.WriteLine(Saludar("Ana"))         ' Hola, Ana!

' Argumento con nombre:
Console.WriteLine(Saludar("Ray", prefijo:="Buenos días"))

Los parámetros con Optional necesitan un valor por defecto. Los argumentos con nombre (prefijo:=) permiten saltar parámetros opcionales con claridad.

Lambda multilínea
Dim factorial = Function(n As Integer)
                    If n <= 1 Then Return 1
                    Return n * factorial(n - 1)
                End Function

Console.WriteLine(factorial(5))  ' 120

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

Las lambdas de varias líneas terminan con End Function (o End Sub). El cuerpo puede tener If, bucles y múltiples Return.

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

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

El Yield devuelve un elemento cada vez, creando una secuencia perezosa. La función retorna IEnumerable(Of T) y solo genera valores al recorrerla.

ParamArray (nº variable de argumentos)
Function SumarTodo(ParamArray nums() As Integer) As Integer
    Dim total = 0
    For Each n In nums
        total += n
    Next
    Return total
End Function

' Llamar con varios argumentos:
Console.WriteLine(SumarTodo(1, 2, 3))       ' 6
Console.WriteLine(SumarTodo(10, 20, 30, 40)) ' 100

ParamArray acepta un número variable de argumentos, tratados como un array dentro de la función. Debe ser el último parámetro de la lista.

Funciones locales (Local Functions)
Function Calcular(n As Integer) As Integer
    ' Función local, solo visible aquí dentro:
    Function Cuadrado(x As Integer) As Integer
        Return x * x
    End Function

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

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

Las funciones locales se declaran dentro de otra Function y solo existen en ese ámbito. Ayudan a organizar lógica auxiliar sin ensuciar la clase.

Funciones 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

Una función recursiva se llama a sí misma. Siempre necesita un caso base (If n <= 1 Then Return n) para parar, si no entra en bucle 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())          ' elimina espacios
Console.WriteLine(s.IndexOf("World")) ' 6

Las strings tienen métodos como ToUpper, Substring, Contains y Replace. Las strings son inmutables: cada método devuelve una nueva string.

Verbatim y escape
' Las comillas dobles se escapan duplicando:
Dim s = "Él dijo ""Hola"""

' Rutas de archivo (sin escape de \):
Dim path = "C:\Users\Ana\docs"

' Nueva línea y tabulación:
Dim texto = "Línea1" & vbCrLf & "Línea2" & vbTab & "fin"

' Constantes útiles:
Console.WriteLine(vbCrLf)    ' nueva línea (Windows)
Console.WriteLine(vbTab)     ' tabulación
Console.WriteLine(vbNullString)  ' string vacía

En VB.NET las comillas se escapan duplicando (""), no con barra. vbCrLf y vbTab son constantes para nueva línea y tabulación.

Trabajar con fechas
Dim hoy = Date.Now
Dim fecha = #2024-01-15#

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

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

' Aritmética de fechas:
Dim manana = hoy.AddDays(1)
Dim diff = (manana - hoy).TotalDays  ' 1

El tipo Date (o DateTime) tiene propiedades Year, Month, Day. Métodos como AddDays suman tiempo y la resta da un TimeSpan.

Split y Join
Dim csv = "Ana,Ray,Mia"

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

' Separar por varios separadores:
Dim frase = "uno dos;tres"
Dim words = frase.Split({" "c, ";"c}, StringSplitOptions.RemoveEmptyEntries)

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

Split divide una string en un array por un separador (el sufijo c indica un Char). String.Join junta elementos con un delimitador.

StringBuilder
Imports System.Text

Dim sb As New StringBuilder()

For i = 1 To 5
    sb.Append("Línea ").AppendLine(i.ToString())
Next

sb.Insert(0, "=== Inicio ===" & vbCrLf)
sb.Replace("Línea", "Item")

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

El StringBuilder es eficiente para construir strings en bucles, pues no crea un objeto nuevo con cada cambio. Usa Append, AppendLine y ToString.

Conversiones de texto
Dim s = "123"

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

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

' Carácter a código:
Dim code = Asc("A")        ' 65
Dim letra = Chr(65)        ' "A"

Parse convierte texto en número (lanza excepción si es inválido). Asc da el código de un carácter y Chr hace lo inverso.

Interpolación de strings
Dim nombre = "Ana"
Dim edad = 30

' Interpolación con $:
Console.WriteLine($"{nombre} tiene {edad} años")
Console.WriteLine($"En 5 años: {edad + 5}")

' Con alineación y formato:
Console.WriteLine($"|{nombre,-10}|{edad,5}|")

' String multilínea:
Dim texto = $"Hola {nombre},
¡bienvenida!"

La interpolación con $ inserta expresiones dentro de {}. Puedes alinear con coma ({nombre,-10}) y formatear con dos puntos.

Comparación de strings
Dim a = "Hola"
Dim b = "hola"

' Sensible a mayúsculas:
Console.WriteLine(a = b)              ' False

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

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

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

Por defecto la comparación distingue mayúsculas. Usa StringComparison.OrdinalIgnoreCase para ignorar la capitalización en Equals y Compare.

Verificar contenido de string
Dim s = "Hola Mundo"

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

' Vacía o nula:
Dim x As String = ""
Console.WriteLine(String.IsNullOrEmpty(x))       ' True
Console.WriteLine(String.IsNullOrWhiteSpace("  ")) ' True

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

StartsWith, EndsWith y Contains verifican contenido. IsNullOrWhiteSpace es la comprobación más segura para entradas del usuario.

String.Format
Dim nombre = "Ana"
Dim edad = 30

' Format con posiciones:
Dim msg = String.Format("{0} tiene {1} años", nombre, edad)

' Reutilizar la misma posición:
Dim r = String.Format("{0} + {0} = {1}", 5, 10)  ' "5 + 5 = 10"

' Formato numérico:
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 sustituye los marcadores {0}, {1} por los argumentos. Los códigos F2, D5 y X formatean números.

Pad, Trim y Remove
Dim s = "  Hola  "

Console.WriteLine(s.Trim())        ' "Hola"
Console.WriteLine(s.TrimStart())   ' "Hola  "
Console.WriteLine(s.TrimEnd())     ' "  Hola"

' Rellenar hasta un tamaño:
Console.WriteLine("42".PadLeft(5, "0"c))   ' "00042"
Console.WriteLine("Hi".PadRight(6, "."c))  ' "Hi...."

' Eliminar parte:
Console.WriteLine("Hola Mundo".Remove(4))      ' "Hola"
Console.WriteLine("Hola Mundo".Remove(4, 3))   ' "Holando"

Trim elimina espacios en los extremos. PadLeft/PadRight rellenan hasta un tamaño. Remove borra a partir de un índice.

Expresiones regulares
Imports System.Text.RegularExpressions

Dim texto = "Contacto: 912345678 o 923456789"

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

' Sustituir:
Dim limpio = Regex.Replace(texto, "\d", "*")

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

La clase Regex hace búsqueda avanzada de patrones. Matches encuentra todas las ocurrencias, Replace sustituye e IsMatch valida.

Classes e OOP


12 cards
Clase y constructor
Public Class Persona
    Public Property Nombre As String
    Public Property Edad As Integer

    ' Constructor:
    Public Sub New(nombre As String, edad As Integer)
        Me.Nombre = nombre
        Me.Edad = edad
    End Sub

    Public Function Saludo() As String
        Return $"Hola, soy {Nombre}"
    End Function
End Class

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

Una clase se define con Class...End Class. El constructor es el método Sub New y Me se refiere a la instancia actual.

NotInheritable y Shadows
' Clase sellada (no puede heredarse):
Public NotInheritable Class Utilitario
    Public Shared Function Version() As String
        Return "1.0"
    End Function
End Class

' Shadows: oculta el miembro de la base:
Public Class Base
    Public Function Info() As String
        Return "Base"
    End Function
End Class

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

NotInheritable impide la herencia (clase sellada). Shadows oculta un miembro heredado con uno nuevo del mismo nombre, sin polimorfismo.

Enum
Public Enum DiaSemana
    Lunes = 1
    Martes
    Miercoles
    Jueves
    Viernes
    Sábado
    Domingo
End Enum

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

' Convertir desde número:
Dim d = CType(3, DiaSemana)   ' Miercoles

Un Enum define un conjunto de constantes con nombre. Los valores son enteros por defecto (empezando en 0 o en el valor indicado). CType convierte números en enum.

Modificadores de acceso
Public Class Cuenta
    Public Saldo As Decimal        ' accesible en todas partes
    Private _código As String      ' solo dentro de la clase
    Protected Titular As String    ' clase y derivadas
    Friend Banco As String         ' solo en el mismo assembly

    Public Sub New()
        _código = GenerarCodigo()  ' accede al privado
    End Sub

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

Public es accesible en todas partes; Private solo en la clase; Protected en la clase y derivadas; Friend solo dentro del mismo 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("Nadando")
    End Sub

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

Una Interface define un contrato sin implementación. La clase usa Implements para adoptarla y vincula cada método con Implements Nombre.Miembro.

Structure (tipo por valor)
Public Structure Punto
    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 Punto(3, 4)
Console.WriteLine(p.Distancia())  ' 5

Una Structure es un tipo por valor (guardado en la stack), ideal para datos pequeños. A diferencia de las clases, no soporta herencia y la copia se hace por valor.

Herencia
Public Class Animal
    Public Property Nombre As String

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

Public Class Perro
    Inherits Animal

    Public Overrides Function Sonido() As String
        Return "¡Guau guau!"
    End Function
End Class

Dim p As New Perro()
Console.WriteLine(p.Sonido())  ' ¡Guau guau!

Inherits crea una clase derivada. Overridable permite redefinir un método en la base; Overrides proporciona la nueva implementación en la derivada.

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

    Public Sub New()
        Total += 1
    End Sub

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

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

Los miembros Shared pertenecen a la clase y no a la instancia. Se accede a ellos por el nombre de la clase (Contador.Total) y se comparten entre todos los objetos.

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

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

Dim c1 As New Caja(Of Integer)(42)
Dim c2 As New Caja(Of String)("hola")

Console.WriteLine(c1.Valor)  ' 42
Console.WriteLine(c2.Valor)  ' hola

Los genéricos usan (Of T) para crear tipos reutilizables con cualquier tipo de dato. El mismo código funciona para Integer, String, etc.

MustInherit y MustOverride
' Clase abstracta (no puede instanciarse):
Public MustInherit Class Forma
    Public MustOverride Function Area() As Double

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

Public Class Círculo
    Inherits Forma
    Public Radio As Double

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

MustInherit hace la clase abstracta (no instanciable). MustOverride declara un método sin cuerpo que las derivadas están obligadas a implementar.

Overrides y Overloads
Public Class Calculadora
    ' Overloads: mismo nombre, parámetros diferentes
    Public Overloads Function Sumar(a As Integer, b As Integer) As Integer
        Return a + b
    End Function

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

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

Overloads permite varios métodos con el mismo nombre pero firmas diferentes (nº o tipo de parámetros). El compilador elige el correcto según la llamada.

Restricciones genéricas
' T debe ser una clase (tipo por referencia):
Public Class Repo(Of T As Class)
End Class

' T debe implementar una interfaz:
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 debe tener constructor sin parámetros:
Public Class Fabrica(Of T As New)
    Public Function Crear() As T
        Return New T()
    End Function
End Class

Las restricciones (As Class, As New, As IComparable) limitan los tipos aceptados por un genérico, permitiendo usar métodos específicos con seguridad.

Avançado


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

' Llamar:
Async Function EjecutarAsync() As Task
    Dim datos = Await DescargarAsync("https://api.ejemplo.com")
    Console.WriteLine(datos)
End Function

Los métodos Async devuelven Task o Task(Of T). El Await pausa sin bloquear el hilo, liberando la interfaz para otras tareas.

Eventos
Public Class Termometro
    Public Event AlCambiar(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 AlCambiar(Me, value)
        End Set
    End Property
End Class

Un Event notifica a los suscriptores cuando algo ocurre. RaiseEvent dispara el evento. Es el patrón usado en interfaces gráficas y observadores.

Reflexión
Imports System.Reflection

Dim tipo = GetType(Persona)

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

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

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

La reflexión (GetType, GetMethods, GetProperties) inspecciona tipos en tiempo de ejecución. La usan serializadores y frameworks.

Task y Task.Run
' Ejecutar trabajo en segundo plano:
Async Function ProcesarAsync() As Task(Of Integer)
    Dim resultado = Await Task.Run(Function()
                                       Threading.Thread.Sleep(1000)
                                       Return 42
                                   End Function)
    Return resultado
End Function

' Varias tareas en paralelo:
Dim t1 = DescargarAsync("url1")
Dim t2 = DescargarAsync("url2")
Await Task.WhenAll(t1, t2)

Task.Run ejecuta código en un hilo del pool. Task.WhenAll espera varias tareas en paralelo, mejorando el rendimiento en operaciones independientes.

AddHandler y Handles
Dim t As New Termometro()

' Suscribirse en tiempo de ejecución:
AddHandler t.AlCambiar,
    Sub(sender, temp) Console.WriteLine($"Temp: {temp}")

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

' O con Handles (en clase):
Private Sub AlCambiarHandler(sender As Object, temp As Double) _
    Handles t.AlCambiar
    Console.WriteLine($"Cambió a {temp}")
End Sub

' Eliminar:
RemoveHandler t.AlCambiar, AddressOf AlCambiarHandler

AddHandler suscribe un evento en tiempo de ejecución; Handles lo vincula en tiempo de compilación. RemoveHandler cancela la suscripción.

Atributos
<Obsolete("Use NuevoMetodo en lugar de este")>
Public Sub MetodoAntiguo()
End Sub

' Atributo con parámetros:
<DebuggerDisplay("Nombre={Nombre}, Edad={Edad}")>
Public Class Persona
    Public Property Nombre As String
    Public Property Edad As Integer
End Class

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

Los atributos (entre < >) añaden metadatos al código. Obsolete marca código obsoleto; DebuggerDisplay personaliza la depuración.

Delegates
' Declarar un delegate:
Public Delegate Function Operacion(a As Integer, b As Integer) As Integer

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

' Usar:
Dim op As Operacion = AddressOf Sumar
Dim r = op(3, 4)   ' 7

' Apuntar a una lambda:
Dim op2 As Operacion = Function(a, b) a * b
Console.WriteLine(op2(3, 4))  ' 12

Un Delegate es un tipo que referencia un método. AddressOf lo vincula a un método existente. Es la base de eventos y callbacks en VB.NET.

Tipos Nullable
' Tipo por valor que puede 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 coalescencia:
Dim y As Integer? = Nothing
Dim resultado = If(y, 0)   ' 0 si y es Nothing

Un tipo Nullable (Integer?) puede contener Nothing además del valor normal. HasValue y Value verifican y obtienen el valor con seguridad.

Operadores Nothing e Is
Dim s As String = Nothing

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

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

' Referencia igual:
Dim a As New Object()
Dim b = a
Console.WriteLine(a Is b)        ' True (mismo objeto)
Console.WriteLine(a IsNot b)     ' False

Usa Is Nothing e IsNot Nothing para verificar referencias nulas. Is compara identidad de objeto, no igualdad de valor.

Action y Func
' Func: devuelve valor (el último tipo es el retorno):
Dim doble As Func(Of Integer, Integer) = Function(x) x * 2
Console.WriteLine(doble(5))   ' 10

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

' Action: no devuelve valor:
Dim imprimir As Action(Of String) = Sub(msg) Console.WriteLine(msg)
imprimir("Hola")

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

Func y Action son delegates listos: Func devuelve un valor, Action no. Evitan declarar delegates personalizados para casos simples.

TypeOf y CType
Dim obj As Object = "Hola"

' Verificar el tipo:
If TypeOf obj Is String Then
    Console.WriteLine("Es una string")
End If

' Convertir (lanza excepción si falla):
Dim s = CType(obj, String)

' Conversión segura (devuelve Nothing si falla):
Dim n = TryCast(obj, Integer)   ' Nothing
If n IsNot Nothing Then
    Console.WriteLine(n)
End If

TypeOf...Is verifica el tipo en tiempo de ejecución. CType convierte (lanza excepción si es imposible); TryCast devuelve Nothing en vez de fallar.

NameOf e interpolación
Public Sub Validar(nombre As String)
    If String.IsNullOrEmpty(nombre) Then
        ' NameOf devuelve el nombre del parámetro:
        Throw New ArgumentException("Inválido", NameOf(nombre))
    End If
End Sub

Console.WriteLine(NameOf(Validar))   ' "Validar"
Console.WriteLine(NameOf(Persona.Nombre))  ' "Nombre"

NameOf devuelve el nombre de un símbolo como string, en tiempo de compilación. Es útil en mensajes de error y notificaciones, evitando strings escritas a mano.

Controlo de Fluxo


12 cards
If / ElseIf / Else
Dim edad = 20

If edad >= 18 Then
    Console.WriteLine("Adulto")
ElseIf edad >= 12 Then
    Console.WriteLine("Adolescente")
Else
    Console.WriteLine("Niño")
End If

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

El bloque If...Then...End If termina siempre con End If. En instrucciones de una línea el End If es opcional.

While y Do Loop
Dim x = 5

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

' Do Until (repite hasta ser verdadero):
x = 0
Do Until x = 10
    x += 1
Loop

' Do While (repite mientras sea verdadero):
Do While x > 0
    x -= 1
Loop

While...End While repite mientras la condición sea verdadera. Do Until repite hasta que la condición sea verdadera; Do While repite mientras lo sea.

Throw y excepciones personalizadas
Sub DefinirEdad(edad As Integer)
    If edad < 0 Then
        Throw New ArgumentException("Edad inválida")
    End If
End Sub

' Excepción personalizada:
Public Class SaldoInsuficienteException
    Inherits Exception

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

Throw New SaldoInsuficienteException("Sin fondos")

Throw lanza una excepción. Puedes crear excepciones propias heredando de Exception y llamando a MyBase.New para pasar el mensaje.

Select Case
Dim dia = 3

Select Case dia
    Case 1
        Console.WriteLine("Lunes")
    Case 2, 3
        Console.WriteLine("Martes o Miércoles")
    Case 4 To 6
        Console.WriteLine("Mitad de semana")
    Case Is >= 7
        Console.WriteLine("Fin de semana")
    Case Else
        Console.WriteLine("Inválido")
End Select

El Select Case es el switch de VB. Soporta valores únicos, listas (Case 2, 3), rangos (Case 4 To 6) y condiciones con Is.

Do con condición al final
Dim n = 0

' La condición se prueba DESPUÉS (ejecuta al menos 1 vez):
Do
    n += 1
    Console.WriteLine(n)
Loop Until n >= 5

Do
    n -= 1
Loop While n > 0

Cuando la condición viene después del Loop, el bloque se ejecuta siempre al menos una vez. Es útil para menús y validaciones de entrada.

When (filtro de excepción)
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("Otro error")
End Try

La cláusula When filtra excepciones por una condición booleana. Solo se ejecuta el Catch cuyo filtro devuelva True.

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

' Cuenta decreciente con Step:
For i = 10 To 1 Step -1
    Console.WriteLine(i)
Next

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

El bucle For...Next repite un bloque un número definido de veces. El Step define el incremento (puede ser negativo para contar hacia atrás).

Exit y Continue
For i = 1 To 10
    If i = 3 Then Continue For  ' salta al siguiente
    If i = 8 Then Exit For      ' sale del bucle
    Console.WriteLine(i)
Next
' Imprime: 1 2 4 5 6 7

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

Continue For salta a la siguiente iteración; Exit For termina el bucle inmediatamente. También existen Exit While, Exit Do y Exit Sub.

Using (recursos automáticos)
Imports System.IO

Using reader As New StreamReader("datos.txt")
    Dim linea = reader.ReadLine()
    Console.WriteLine(linea)
End Using
' reader se cierra/libera automáticamente aquí

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

El bloque Using garantiza que objetos con recursos (archivos, conexiones) se liberan automáticamente al final, incluso si ocurre una excepción.

For Each
Dim frutas = {"Manzana", "Pera", "Uva"}

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

' Con índice (si necesitas la posición):
For i = 0 To frutas.Length - 1
    Console.WriteLine($"{i}: {frutas(i)}")
Next

El For Each recorre cada elemento de una colección sin necesidad de índice. Usa el For normal cuando necesites la posición del elemento.

Try / Catch / Finally
Try
    Dim r = 10 \ 0
Catch ex As DivideByZeroException
    Console.WriteLine("¡División por cero!")
Catch ex As Exception
    Console.WriteLine($"Error: {ex.Message}")
Finally
    Console.WriteLine("Se ejecuta siempre")
End Try

El bloque Try protege código que puede fallar. El Catch captura la excepción (puede haber varios, del más específico al general) y el Finally se ejecuta siempre.

With (acceso a miembros)
Dim p As New Persona()

With p
    .Nombre = "Ana"
    .Edad = 30
    .Email = "ana@mail.com"
End With

' Equivalente a:
' p.Nombre = "Ana"
' p.Edad = 30
' p.Email = "ana@mail.com"

Console.WriteLine(p.Nombre)

El bloque With evita repetir el nombre del objeto al definir varias propiedades. El punto . se refiere al objeto del With.

Coleções e LINQ


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

' Acceso (el índice empieza en 0):
Console.WriteLine(nums(0))   ' 1
nums(0) = 10

' Tamaño:
Console.WriteLine(nums.Length)  ' 5

Un array tiene tamaño fijo e índice que empieza en 0. Length devuelve el número de elementos. Los valores se definen entre llaves {}.

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

lista.Add("Ana")
lista.Add("Ray")
lista.Insert(0, "Mia")     ' inserta al inicio
lista.Remove("Ana")        ' elimina por valor
lista.RemoveAt(0)          ' elimina por índice

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

La List(Of T) es un array dinámico que crece automáticamente. Tiene Add, Insert, Remove y la propiedad Count.

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

conjunto.Add(3)    ' ignorado (ya existe)
conjunto.Add(4)

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

' Operaciones 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}

El HashSet guarda valores únicos sin duplicados y con verificación rápida de existencia. Tiene operaciones como IntersectWith, UnionWith y ExceptWith.

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

' Redimensionar (pierde los datos):
ReDim nums(9)

' Redimensionar manteniendo los datos:
Dim lista(2) As String
lista(0) = "A"
lista(1) = "B"
ReDim Preserve lista(5)   ' mantiene A y B

Console.WriteLine(lista.Length)  ' 6

ReDim cambia el tamaño del array. Sin Preserve los datos se pierden; con Preserve se mantienen los elementos existentes.

List - búsqueda y ordenación
Dim nums As New List(Of Integer) From {5, 2, 8, 1, 9}

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

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

' Eliminar con condición:
nums.RemoveAll(Function(n) n < 3)

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

Find devuelve el primer elemento que satisface el predicado; FindAll devuelve todos. RemoveAll y Exists también reciben una lambda.

LINQ - sintaxis 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

La sintaxis de consulta LINQ parece SQL: From define la fuente, Where filtra, Order By ordena y Select proyecta el resultado.

Operaciones con arrays
Dim nums = {5, 2, 8, 1, 9}

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

Dim pos = Array.IndexOf(nums, 5)   ' posición de 5
Dim existe = nums.Contains(8)      ' True

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

La clase Array ofrece Sort, Reverse e IndexOf. Los métodos de extensión LINQ como Sum, Max y Average hacen cálculos rápidos.

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

edades("Ana") = 30
edades.Add("Ray", 25)
edades("Mia") = 22

Console.WriteLine(edades("Ana"))         ' 30
Console.WriteLine(edades.ContainsKey("Ray"))  ' True
Console.WriteLine(edades.Count)          ' 3

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

El Dictionary guarda pares clave/valor con acceso rápido por la clave. TryGetValue evita una excepción cuando la clave no existe.

LINQ - sintaxis 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()

' Otros métodos útiles:
Dim suma = nums.Sum()
Dim pares = nums.Count(Function(n) n Mod 2 = 0)
Dim primero = nums.First(Function(n) n > 3)

La sintaxis de método encadena operadores como Where, OrderBy y Take usando lambdas. Sum, Count y First hacen agregaciones.

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

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

' Recorrer:
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

Los arrays multidimensionales usan coma en la declaración: (,) para 2D. GetLength(0) da el nº de filas y GetLength(1) el nº de columnas.

Recorrer Dictionary
Dim edades As New Dictionary(Of String, Integer) From {
    {"Ana", 30},
    {"Ray", 25},
    {"Mia", 22}
}

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

' Solo las claves / solo los valores:
For Each nombre In edades.Keys
    Console.WriteLine(nombre)
Next

Al recorrer un Dictionary cada elemento es un par con Key y Value. Las propiedades Keys y Values dan acceso por separado.

LINQ - proyección y agrupación
Dim personas = GetPersonas()

' Proyección (Select):
Dim nombres = personas.Select(Function(p) p.Nombre).ToList()

' Agrupación:
Dim porCiudad = From p In personas
                Group By p.Ciudad Into Grupo = Group
                Select Ciudad, Total = Grupo.Count()

' Distintos:
Dim ciudades = personas.Select(Function(p) p.Ciudad).Distinct()

Select transforma cada elemento. Group By...Into Group agrupa por una clave y Distinct elimina duplicados del resultado.

Entrada/Saída e Ficheiros


10 cards
Entrada y salida en la consola
' Salida:
Console.WriteLine("Con nueva línea")
Console.Write("Sin nueva línea")
Console.WriteLine($"Valor: {42}")

' Entrada:
Console.Write("Nombre: ")
Dim nombre = Console.ReadLine()

' Leer un número:
Console.Write("Edad: ")
Dim edad = CInt(Console.ReadLine())

' Limpiar y colores:
Console.ForegroundColor = ConsoleColor.Green
Console.WriteLine("¡Éxito!")
Console.ResetColor()

Console.WriteLine escribe con nueva línea; ReadLine lee una línea como string. ForegroundColor cambia el color del texto en la consola.

Rutas (Path)
Imports System.IO

Dim ruta = "C:\datos\informe.txt"

Console.WriteLine(Path.GetFileName(ruta))    ' "informe.txt"
Console.WriteLine(Path.GetExtension(ruta))   ' ".txt"
Console.WriteLine(Path.GetFileNameWithoutExtension(ruta))
Console.WriteLine(Path.GetDirectoryName(ruta))

' Unir rutas con seguridad:
Dim nueva = Path.Combine("C:\datos", "sub", "f.txt")

' Archivo temporal:
Dim temp = Path.GetTempFileName()

La clase Path manipula rutas sin errores de separadores. Path.Combine une partes con seguridad y GetExtension obtiene la extensión.

Acceder a base de datos
Imports System.Data.SqlClient

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

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

Acceso ADO.NET: SqlConnection conecta a la base de datos, SqlCommand ejecuta la query y ExecuteReader recorre los resultados. Usa siempre Using.

Escribir archivos
Imports System.IO

' Escribir todo de una vez:
File.WriteAllText("f.txt", "Hola Mundo")

' Varias líneas:
File.WriteAllLines("f.txt", {"L1", "L2", "L3"})

' Añadir al final:
File.AppendAllText("log.txt", "nueva línea" & vbCrLf)

' Con StreamWriter (más control):
Using sw As New StreamWriter("f.txt")
    sw.WriteLine("Línea 1")
    sw.WriteLine("Línea 2")
End Using

File.WriteAllText y WriteAllLines crean/sobrescriben archivos. AppendAllText añade al final. El StreamWriter da más control en escritura progresiva.

Listar archivos y carpetas
Imports System.IO

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

' Con filtro y recursión:
Dim txts = Directory.GetFiles("C:\datos", "*.txt",
    SearchOption.AllDirectories)

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

' Versión lazy (memoria eficiente):
For Each f In Directory.EnumerateFiles("C:\datos", "*.log")
    Console.WriteLine(f)
Next

GetFiles y GetDirectories listan contenidos. El filtro *.txt selecciona por extensión y SearchOption.AllDirectories búsqueda recursivamente.

Parámetros SQL (anti-inyección)
Using conn As New SqlConnection(connStr)
    conn.Open()
    Dim sql = "SELECT * FROM Cliente WHERE Edad > @edad"
    Using cmd As New SqlCommand(sql, conn)
        cmd.Parameters.AddWithValue("@edad", 18)

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

' Insertar con parámetros:
Dim ins = "INSERT INTO Cliente (Nombre) VALUES (@nombre)"

Usa siempre parámetros (@edad) en vez de concatenar strings en las queries. Esto previene la inyección de SQL. ExecuteScalar devuelve un único valor.

Leer archivos
Imports System.IO

' Leer todo como texto:
Dim contenido = File.ReadAllText("f.txt")

' Leer como array de líneas:
Dim lineas = File.ReadAllLines("f.txt")
For Each linea In lineas
    Console.WriteLine(linea)
Next

' Con StreamReader (línea a línea):
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 devuelve el archivo entero; ReadAllLines devuelve un array de líneas. El StreamReader es eficiente para archivos grandes, leyendo línea a línea.

Serialización JSON
Imports System.Text.Json

Public Class Persona
    Public Property Nombre As String
    Public Property Edad As Integer
End Class

' Serializar:
Dim p As New Persona With {.Nombre = "Ana", .Edad = 30}
Dim json = JsonSerializer.Serialize(p)

' Deserializar:
Dim p2 = JsonSerializer.Deserialize(Of Persona)(json)
Console.WriteLine(p2.Nombre)  ' Ana

JsonSerializer.Serialize convierte un objeto en JSON; Deserialize(Of T) hace lo inverso. Está incluido en System.Text.Json en el .NET moderno.

Verificar y gestionar archivos
Imports System.IO

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

If Directory.Exists("carpeta") Then
    Console.WriteLine("La carpeta existe")
End If

' Crear carpeta:
Directory.CreateDirectory("datos/logs")

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

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

File.Exists y Directory.Exists verifican existencia. CreateDirectory crea carpetas; Delete, Copy y Move gestionan archivos.

Serialización XML
Imports System.Xml.Serialization

<Serializable()>
Public Class Producto
    Public Property Nombre As String
    Public Property Precio As Decimal
End Class

Dim serializer As New XmlSerializer(GetType(Producto))

Using sw As New StreamWriter("producto.xml")
    serializer.Serialize(sw, New Producto With {.Nombre = "Libro", .Precio = 9.9})
End Using

El XmlSerializer convierte objetos en XML y viceversa. El tipo debe ser serializable y tener un constructor sin parámetros. Usa Serialize y Deserialize.

Tipos e Recursos Extra


10 cards
Tipos anónimos
Dim persona = New With {
    .Nombre = "Ana",
    .Edad = 30
}

Console.WriteLine(persona.Nombre)   ' Ana
Console.WriteLine(persona.Edad)     ' 30

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

Los tipos anónimos (New With { }) crean objetos sin definir una clase. Son útiles en proyecciones LINQ para resultados temporales con propiedades con nombre.

Math (matemáticas)
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

La clase Math tiene constantes (PI, E) y funciones como Abs, Pow, Sqrt, Round, Floor y Ceiling.

Colecciones especializadas
Imports System.Collections.Generic

' Queue (FIFO - primero en entrar, primero en salir):
Dim cola As New Queue(Of String)
cola.Enqueue("A")
cola.Enqueue("B")
Console.WriteLine(cola.Dequeue())  ' A

' Stack (LIFO - último en entrar, primero en salir):
Dim pila As New Stack(Of Integer)
pila.Push(1)
pila.Push(2)
Console.WriteLine(pila.Pop())  ' 2

Queue es una cola (FIFO) con Enqueue/Dequeue. Stack es una pila (LIFO) con Push/Pop. Elige según el orden necesario.

Tuplas
' Tupla con nombres:
Dim persona = (Nombre:="Ana", Edad:=30)
Console.WriteLine(persona.Nombre)   ' Ana
Console.WriteLine(persona.Edad)     ' 30

' Devolver varios valores de una función:
Function Dividir(a As Integer, b As Integer) As (Cociente As Integer, Resto As Integer)
    Return (a \ b, a Mod b)
End Function

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

Las tuplas agrupan varios valores sin crear una clase. Con nombres (Nombre:=) los campos quedan legibles. Son ideales para devolver múltiples valores de una función.

Random (aleatorios)
Dim rnd As New Random()

' Entero aleatorio:
Dim n = rnd.Next()              ' 0 hasta Integer.MaxValue
Dim dado = rnd.Next(1, 7)       ' 1 a 6

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

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

' Compartir una instancia (evita repetición):
Shared ReadOnly Rnd As New Random()

La clase Random genera números aleatorios. Next(min, max) devuelve un entero en el intervalo (máx exclusivo). Reutiliza la instancia para evitar valores repetidos.

Conversiones de colección
Dim lista As New List(Of Integer) From {1, 2, 3}

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

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

' List a Dictionary:
Dim personas = GetPersonas()
Dim dict = personas.ToDictionary(
    Function(p) p.Id,
    Function(p) p.Nombre)

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

ToArray y ToList convierten entre colecciones. ToDictionary crea un diccionario a partir de una lista usando funciones para clave y valor.

Colecciones inmutables
Imports System.Collections.Immutable

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

' "Cambiar" devuelve una nueva lista:
Dim lista2 = lista.Add(4)

Console.WriteLine(lista.Count)   ' 3 (sin cambios)
Console.WriteLine(lista2.Count)  ' 4

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

Las colecciones inmutables (ImmutableList, ImmutableDictionary) nunca cambian: cada operación devuelve una nueva colección. Son seguras en código concurrente.

Guid (identificador único)
' Generar un GUID:
Dim id = Guid.NewGuid()
Console.WriteLine(id)   ' ej: 3f2a...-...-...

' Como string sin guiones:
Dim s = id.ToString("N")

' GUID vacío:
Dim vacio = Guid.Empty
Console.WriteLine(id = Guid.Empty)  ' False

' Convertir desde string:
Dim g = Guid.Parse("3f2a1b...")

Guid.NewGuid() genera un identificador único global de 128 bits. Se usa como clave primaria y para nombres únicos. Guid.Empty es el valor nulo.

Conversiones avanzadas
' Enum a string y viceversa:
Dim dia = DiaSemana.Viernes
Dim nombre = dia.ToString()             ' "Viernes"
Dim vuelta = [Enum].Parse(GetType(DiaSemana), "Viernes")

' Array de bytes a Base64:
Dim bytes = {1, 2, 3, 4}
Dim b64 = Convert.ToBase64String(bytes)
Dim orig = Convert.FromBase64String(b64)

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

Enum.Parse convierte texto en enum. Convert.ToBase64String codifica bytes en Base64. La clase Convert hace conversiones entre tipos diversos.

TimeSpan
' Crear un intervalo de tiempo:
Dim duracion = New TimeSpan(1, 30, 0)   ' 1h 30m
Console.WriteLine(duracion.TotalMinutes)  ' 90

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

' Diferencia entre fechas:
Dim inicio = #2024-01-01#
Dim fin = #2024-03-15#
Dim días = (fin - inicio).TotalDays
Console.WriteLine($"{días} días")

TimeSpan representa una duración. TotalMinutes y TotalHours convierten a unidades. La diferencia entre dos fechas devuelve un TimeSpan.

Dicas e Boas Práticas


8 cards
Inicializadores de objeto
' Sin constructor, definiendo propiedades:
Dim p As New Persona With {
    .Nombre = "Ana",
    .Edad = 30
}

' Colección con inicializador:
Dim personas As New List(Of Persona) From {
    New Persona With {.Nombre = "Ray", .Edad = 25},
    New Persona With {.Nombre = "Mia", .Edad = 22}
}

Console.WriteLine(personas.Count)  ' 2

El bloque With { } inicializa propiedades al crear el objeto, sin necesitar constructor. Se combina con From { } para rellenar colecciones de forma legible.

Tratamiento defensivo
Sub Procesar(lista As List(Of String))
    ' Validar argumentos pronto:
    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

Valida los argumentos al inicio con ArgumentNullException. Usa Return y Continue For para salir pronto de casos inválidos, manteniendo el código principal sin anidamientos.

Operador de coalescencia y Elvis
Dim nombre As String = Nothing

' If() con 2 args: primero no-Nothing:
Dim mostrado = If(nombre, "Anónimo")

' Acceso seguro con ?.:
Dim longitud = nombre?.Length   ' Nothing (sin error)

' Combinación útil:
Dim tam = nombre?.Length ?? -1
' En VB se usa If anidado:
Dim tam2 = If(nombre IsNot Nothing, nombre.Length, -1)

El operador ?. (null-conditional) solo accede al miembro si el objeto no es Nothing, evitando NullReferenceException. If(a, b) hace coalescencia.

Patrón Repository (ejemplo)
Public Interface IRepositorio(Of T)
    Function Obtener(id As Integer) As T
    Function Todos() As List(Of T)
    Sub Guardar(entidad As T)
    Sub Eliminar(id As Integer)
End Interface

Public Class ClienteRepositorio
    Implements IRepositorio(Of Cliente)

    Public Function Obtener(id As Integer) As Cliente _
        Implements IRepositorio(Of Cliente).Obtener
        ' acceder a la BD...
        Return New Cliente()
    End Function
End Class

El patrón Repository abstrae el acceso a datos detrás de una interfaz genérica IRepositorio(Of T). Facilita los tests y el cambio de fuente de datos sin modificar la lógica.

Patrón Dispose e IDisposable
Public Class Conexion
    Implements IDisposable

    Private _abierta As Boolean = True

    Public Sub Cerrar()
        If _abierta Then
            _abierta = False
            Console.WriteLine("Cerrada")
        End If
    End Sub

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

' Uso con Using:
Using c As New Conexion()
    ' ...
End Using

Las clases con recursos implementan IDisposable con el método Dispose. El Using llama a Dispose automáticamente, garantizando la liberación.

Patrón Singleton
Public Class Configuracion
    ' Instancia única, creada de forma thread-safe:
    Private Shared ReadOnly _instancia As New Lazy(Of Configuracion)(
        Function() New Configuracion())

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

    Private Sub New()   ' constructor privado
    End Sub

    Public Property Tema As String = "Claro"
End Class

Dim cfg = Configuracion.Instancia

El Singleton garantiza una única instancia. El constructor Private impide la creación externa y Lazy(Of T) crea la instancia solo cuando es necesaria, de forma thread-safe.

Convenciones de nomenclatura
' Clases, métodos, propiedades: PascalCase
Public Class ClienteFidelizado
    Public Property NombreCompleto As String

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

' Variables locales y parámetros: camelCase
Dim totalCompra As Decimal
Sub Procesar(valor As Decimal)
End Sub

' Campos privados: _camelCase
Private _saldo As Decimal

Usa PascalCase para clases, métodos y propiedades; camelCase para variables locales y parámetros; prefijo _ para campos privados. Las interfaces empiezan por I.

Buenas prácticas generales
' 1. Option Strict On siempre
Option Strict On

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

' 3. Preferir colecciones genéricas
Dim lista As New List(Of Integer)  ' no ArrayList

' 4. Async en vez de bloquear
Dim datos = Await ObtenerAsync()   ' no .Result

' 5. Excepciones específicas
Try
    ' ...
Catch ex As IOException
    ' tratar
End Try

Activa Option Strict On, usa Using para recursos, prefiere colecciones genéricas, usa Await en vez de bloquear y captura excepciones específicas.