Manual Completo de Programación en Visual Basic

1. Introducción a Visual Basic

Visual Basic (VB) es un lenguaje de programación desarrollado por Microsoft que se caracteriza por su sintaxis sencilla y su enfoque en la productividad del desarrollador.

1.1 Historia y versiones

1.2 Entornos de desarrollo

Para programar en VB puedes usar:

2. Fundamentos de Visual Basic

2.1 Estructura básica de un programa

Module Module1
    Sub Main()
        ' Este es un programa básico en VB
        Console.WriteLine("¡Hola Mundo!")
        Console.ReadKey()
    End Sub
End Module

2.2 Comentarios

' Esto es un comentario de una línea

''' <summary>
''' Esto es un comentario XML para documentación
''' </summary>

/*
Este es un comentario
de múltiples líneas (en versiones recientes)
*/

2.3 Variables y tipos de datos

VB es un lenguaje de tipado fuerte pero con opción de tipado dinámico.

Tipos de datos básicos:

Dim entero As Integer = 10          ' Entero de 32 bits
Dim largo As Long = 1000000      ' Entero de 64 bits
Dim decimal As Decimal = 10.99D  ' Decimal de 128 bits
Dim doble As Double = 10.99R     ' Doble precisión (64 bits)
Dim simple As Single = 10.99F    ' Simple precisión (32 bits)
Dim caracter As Char = "A"c      ' Carácter Unicode
Dim cadena As String = "Hola"    ' Cadena de texto
Dim booleano As Boolean = True   ' Valor lógico
Dim fecha As Date = #12/31/2023# ' Fecha

Nota: En VB.NET puedes usar Dim x = 10 y el compilador inferirá el tipo (en este caso Integer).

2.4 Constantes

Const PI As Double = 3.1415926535
Const MENSAJE As String = "Bienvenido"

2.5 Operadores

Operadores aritméticos:

Dim suma As Integer = 5 + 3       ' 8
Dim resta As Integer = 5 - 3     ' 2
Dim multiplicacion = 5 * 3       ' 15
Dim division = 5 / 3             ' 1.666...
Dim divisionEntera = 5 \ 3       ' 1
Dim modulo = 5 Mod 3             ' 2
Dim potencia = 5 ^ 3             ' 125

Operadores de comparación:

Dim igual As Boolean = (5 = 3)   ' False
Dim diferente = (5 <> 3)        ' True
Dim mayorQue = (5 > 3)           ' True
Dim menorQue = (5 < 3)           ' False
Dim mayorIgual = (5 >= 3)        ' True
Dim menorIgual = (5 <= 3)        ' False

Operadores lógicos:

Dim andResult = (True And False)  ' False
Dim orResult = (True Or False)    ' True
Dim notResult = Not True          ' False
Dim xorResult = (True Xor False) ' True
Dim andAlso = (True AndAlso False) ' False (cortocircuito)
Dim orElse = (True OrElse False)  ' True (cortocircuito)

Tip: AndAlso y OrElse son versiones de cortocircuito que mejoran el rendimiento al no evaluar el segundo operando si el primero ya determina el resultado.

3. Estructuras de control

3.1 Condicionales

If...Then...Else

' Forma simple
If edad >= 18 Then
    Console.WriteLine("Mayor de edad")
End If

' Con Else
If temperatura > 30 Then
    Console.WriteLine("Hace calor")
Else
    Console.WriteLine("No hace calor")
End If

' Con ElseIf
If nota >= 90 Then
    Console.WriteLine("A")
ElseIf nota >= 80 Then
    Console.WriteLine("B")
ElseIf nota >= 70 Then
    Console.WriteLine("C")
Else
    Console.WriteLine("Reprobado")
End If

Select Case

Select Case diaSemana
    Case 1
        Console.WriteLine("Lunes")
    Case 2
        Console.WriteLine("Martes")
    Case 3 To 5
        Console.WriteLine("Miércoles a Viernes")
    Case 6, 7
        Console.WriteLine("Fin de semana")
    Case Else
        Console.WriteLine("Día no válido")
End Select

3.2 Bucles

For...Next

' Bucle ascendente
For i As Integer = 1 To 10
    Console.WriteLine(i)
Next

' Con paso
For i = 10 To 1 Step -1
    Console.WriteLine(i)
Next

' Salir anticipadamente
For i = 1 To 100
    If i = 50 Then Exit For
    Console.WriteLine(i)
Next

For Each

Dim frutas As String() = {"Manzana", "Banana", "Naranja"}
For Each fruta As String In frutas
    Console.WriteLine(fruta)
Next

While...End While

Dim contador As Integer = 0
While contador < 5
    Console.WriteLine(contador)
    contador += 1
End While

Do...Loop

' Versión con comprobación al inicio (como While)
Dim x As Integer = 0
Do While x < 5
    Console.WriteLine(x)
    x += 1
Loop

' Versión con comprobación al final (ejecuta al menos una vez)
Dim y As Integer = 0
Do
    Console.WriteLine(y)
    y += 1
Loop Until y >= 5

4. Procedimientos y Funciones

4.1 Subprocedimientos (Sub)

Los Sub no devuelven valores.

Sub Saludar(nombre As String)
    Console.WriteLine("Hola " & nombre)
End Sub

' Llamada
Saludar("Juan")

4.2 Funciones (Function)

Las funciones devuelven valores.

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

' Llamada
Dim resultado As Integer = Sumar(5, 3)

4.3 Parámetros

Parámetros opcionales

Sub MostrarMensaje(mensaje As String, Optional color As ConsoleColor = ConsoleColor.White)
    Console.ForegroundColor = color
    Console.WriteLine(mensaje)
    Console.ResetColor()
End Sub

' Llamadas
MostrarMensaje("Hola")                           ' Usa color blanco por defecto
MostrarMensaje("Advertencia", ConsoleColor.Yellow)

Parámetros por valor vs. por referencia

' Por valor (predeterminado) - Se pasa una copia
Sub IncrementarPorValor(ByVal x As Integer)
    x += 1
End Sub

' Por referencia - Se pasa la variable original
Sub IncrementarPorReferencia(ByRef x As Integer)
    x += 1
End Sub

' Uso
Dim num As Integer = 5
IncrementarPorValor(num)       ' num sigue siendo 5
IncrementarPorReferencia(num)  ' num ahora es 6

4.4 Sobrecarga de métodos

Function CalcularArea(lado As Double) As Double
    Return lado * lado
End Function

Function CalcularArea(base As Double, altura As Double) As Double
    Return base * altura
End Function

' El compilador elige la versión adecuada según los parámetros
Dim areaCuadrado = CalcularArea(5.0)        ' Usa la primera versión
Dim areaRectangulo = CalcularArea(4.0, 6.0) ' Usa la segunda versión

5. Arreglos y Colecciones

5.1 Arreglos

Arreglos unidimensionales

' Declaración e inicialización
Dim numeros(4) As Integer           ' Índices 0 a 4
Dim dias() As String = {"Lun", "Mar", "Mié"}

' Asignación
numeros(0) = 10
numeros(1) = 20

' Recorrido
For i As Integer = 0 To numeros.Length - 1
    Console.WriteLine(numeros(i))
Next

Arreglos multidimensionales

' Matriz 2x3
Dim matriz(1, 2) As Integer

' Inicialización
matriz(0, 0) = 1
matriz(0, 1) = 2
matriz(0, 2) = 3
matriz(1, 0) = 4
matriz(1, 1) = 5
matriz(1, 2) = 6

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

Arreglos jagged (irregulares)

' Arreglo de arreglos
Dim jagged(2)() As Integer
jagged(0) = New Integer() {1, 2, 3}
jagged(1) = New Integer() {4, 5}
jagged(2) = New Integer() {6, 7, 8, 9}

' Recorrido
For i As Integer = 0 To jagged.Length - 1
    For j As Integer = 0 To jagged(i).Length - 1
        Console.Write(jagged(i)(j) & " ")
    Next
    Console.WriteLine()
Next

5.2 Colecciones

Listas genéricas

Imports System.Collections.Generic

Dim nombres As New List(Of String)
nombres.Add("Ana")
nombres.Add("Carlos")
nombres.Add("Beatriz")

' Ordenar
nombres.Sort()

' Recorrido
For Each nombre As String In nombres
    Console.WriteLine(nombre)
Next

Diccionarios

Dim capitales As New Dictionary(Of String, String)
capitales.Add("Argentina", "Buenos Aires")
capitales.Add("Chile", "Santiago")
capitales.Add("Uruguay", "Montevideo")

' Acceso
Console.WriteLine(capitales("Argentina"))  ' Buenos Aires

' Verificar existencia
If capitales.ContainsKey("Brasil") Then
    Console.WriteLine(capitales("Brasil"))
End If

Otras colecciones útiles

6. Programación Orientada a Objetos

6.1 Clases y Objetos

Definición de clase básica

Public Class Persona
    ' Campos (variables miembro)
    Private _nombre As String
    Private _edad As Integer
    
    ' Propiedades
    Public Property Nombre As String
        Get
            Return _nombre
        End Get
        Set(value As String)
            _nombre = value
        End Set
    End Property
    
    Public Property Edad As Integer
        Get
            Return _edad
        End Get
        Set(value As Integer)
            If value >= 0 Then
                _edad = value
            End If
        End Set
    End Property
    
    ' Constructor
    Public Sub New(nombre As String, edad As Integer)
        Me.Nombre = nombre
        Me.Edad = edad
    End Sub
    
    ' Método
    Public Sub Saludar()
        Console.WriteLine($"Hola, soy {Nombre} y tengo {Edad} años.")
    End Sub
End Class

' Uso
Dim persona1 As New Persona("Juan", 30)
persona1.Saludar()

6.2 Herencia

Public Class Empleado
    Inherits Persona
    
    Public Property Salario As Decimal
    Public Property Puesto As String
    
    Public Sub New(nombre As String, edad As Integer, salario As Decimal, puesto As String)
        MyBase.New(nombre, edad)
        Me.Salario = salario
        Me.Puesto = puesto
    End Sub
    
    ' Sobrescritura de método
    Public Overrides Sub Saludar()
        Console.WriteLine($"Hola, soy {Nombre}, tengo {Edad} años y trabajo como {Puesto}.")
    End Sub
End Class

' Uso
Dim empleado1 As New Empleado("María", 28, 2500.0, "Desarrolladora")
empleado1.Saludar()

6.3 Interfaces

Public Interface IRepositorio
    Sub Guardar(entidad As Object)
    Function ObtenerPorId(id As Integer) As Object
    Sub Eliminar(id As Integer)
End Interface

Public Class RepositorioPersonas
    Implements IRepositorio
    
    Public Sub Guardar(entidad As Object) Implements IRepositorio.Guardar
        ' Implementación específica para Personas
    End Sub
    
    Public Function ObtenerPorId(id As Integer) As Object Implements IRepositorio.ObtenerPorId
        ' Implementación
        Return Nothing
    End Function
    
    Public Sub Eliminar(id As Integer) Implements IRepositorio.Eliminar
        ' Implementación
    End Sub
End Class

6.4 Polimorfismo

Public Class Figura
    Public Overridable Sub Dibujar()
        Console.WriteLine("Dibujando figura genérica")
    End Sub
End Class

Public Class Circulo
    Inherits Figura
    
    Public Overrides Sub Dibujar()
        Console.WriteLine("Dibujando círculo")
    End Sub
End Class

Public Class Cuadrado
    Inherits Figura
    
    Public Overrides Sub Dibujar()
        Console.WriteLine("Dibujando cuadrado")
    End Sub
End Class

' Uso polimórfico
Dim figuras As New List(Of Figura)
figuras.Add(New Figura())
figuras.Add(New Circulo())
figuras.Add(New Cuadrado())

For Each figura As Figura In figuras
    figura.Dibujar()
Next

6.5 Clases abstractas

Public MustInherit Class Animal
    Public MustOverride Sub HacerSonido()
    
    Public Sub Dormir()
        Console.WriteLine("Zzzzz...")
    End Sub
End Class

Public Class Perro
    Inherits Animal
    
    Public Overrides Sub HacerSonido()
        Console.WriteLine("¡Guau guau!")
    End Sub
End Class

Public Class Gato
    Inherits Animal
    
    Public Overrides Sub HacerSonido()
        Console.WriteLine("¡Miau!")
    End Sub
End Class

7. Manejo de Excepciones

7.1 Try...Catch...Finally

Try
    ' Código que puede lanzar excepciones
    Dim divisor As Integer = 0
    Dim resultado = 10 / divisor
    Console.WriteLine(resultado)
    
Catch ex As DivideByZeroException
    Console.WriteLine("Error: División por cero")
    
Catch ex As OverflowException
    Console.WriteLine("Error: Desbordamiento")
    
Catch ex As Exception
    Console.WriteLine($"Error inesperado: {ex.Message}")
    
Finally
    ' Código que siempre se ejecuta
    Console.WriteLine("Finalizando operación")
End Try

7.2 Lanzar excepciones

Function CalcularRaizCuadrada(numero As Double) As Double
    If numero < 0 Then
        Throw New ArgumentException("No se puede calcular la raíz de un número negativo")
    End If
    Return Math.Sqrt(numero)
End Function

7.3 Excepciones personalizadas

Public Class SaldoInsuficienteException
    Inherits Exception
    
    Public Sub New(mensaje As String)
        MyBase.New(mensaje)
    End Sub
    
    Public Sub New(mensaje As String, innerException As Exception)
        MyBase.New(mensaje, innerException)
    End Sub
End Class

Public Class CuentaBancaria
    Private _saldo As Decimal
    
    Public Sub Retirar(monto As Decimal)
        If monto > _saldo Then
            Throw New SaldoInsuficienteException("Saldo insuficiente para realizar el retiro")
        End If
        _saldo -= monto
    End Sub
End Class

8. Temas Avanzados

8.1 LINQ (Language Integrated Query)

Dim numeros As Integer() = {5, 10, 15, 20, 25, 30}

' Consulta LINQ básica
Dim mayoresA15 = From num In numeros
                 Where num > 15
                 Order By num Descending
                 Select num

' Métodos de extensión
Dim pares = numeros.Where(Function(n) n Mod 2 = 0).OrderBy(Function(n) n)

' Ejecución diferida vs inmediata
Dim consulta = numeros.Where(Function(n) n > 10) ' Diferida
Dim lista = consulta.ToList()                     ' Inmediata

' LINQ to Objects
Dim personas As New List(Of Persona)
' ... llenar la lista ...

Dim adultos = From p In personas
              Where p.Edad >= 18
              Select p.Nombre, p.Edad
              Order By Edad

8.2 Expresiones Lambda

' Delegado simple
Dim sumar As Func(Of Integer, Integer, Integer) = Function(a, b) a + b
Console.WriteLine(sumar(5, 3)) ' 8

' Uso con métodos
Dim numeros As Integer() = {1, 2, 3, 4, 5}
Dim cuadrados = numeros.Select(Function(n) n * n)

' Expresión multilínea
Dim factorial As Func(Of Integer, Integer) = Function(n)
                                                Dim result = 1
                                                For i = 1 To n
                                                    result *= i
                                                Next
                                                Return result
                                            End Function

8.3 Async/Await

Async Function DescargarContenidoAsync(url As String) As Task(Of String)
    Using cliente As New HttpClient()
        Dim resultado = Await cliente.GetStringAsync(url)
        Return resultado
    End Using
End Function

' Uso
Async Sub BotonDescargar_Click(sender As Object, e As EventArgs) Handles BotonDescargar.Click
    Try
        Dim contenido = Await DescargarContenidoAsync("https://ejemplo.com")
        TextBoxContenido.Text = contenido
    Catch ex As Exception
        MessageBox.Show($"Error al descargar: {ex.Message}")
    End Try
End Sub

8.4 Reflection

Dim tipo As Type = GetType(Persona)

' Obtener propiedades
For Each prop As PropertyInfo In tipo.GetProperties()
    Console.WriteLine($"Propiedad: {prop.Name}, Tipo: {prop.PropertyType.Name}")
Next

' Crear instancia dinámicamente
Dim instancia As Object = Activator.CreateInstance(tipo)

' Invocar método dinámicamente
Dim metodo = tipo.GetMethod("Saludar")
metodo.Invoke(instancia, Nothing)

8.5 Atributos

<Serializable>
Public Class Producto
    <Obsolete("Usar PrecioConDescuento en su lugar")>
    Public Property Precio As Decimal
    
    Public Property PrecioConDescuento As Decimal
    
    <CustomAttribute("Información adicional")>
    Public Property Nombre As String
End Class

' Atributo personalizado
<AttributeUsage(AttributeTargets.Property)>
Public Class CustomAttribute
    Inherits Attribute
    
    Public Property Descripcion As String
    
    Public Sub New(descripcion As String)
        Me.Descripcion = descripcion
    End Sub
End Class

9. Desarrollo de Aplicaciones

9.1 Aplicaciones de Consola

Module Program
    Sub Main(args As String())
        Console.WriteLine("Aplicación de consola")
        Console.WriteLine("Argumentos recibidos:")
        
        For Each arg In args
            Console.WriteLine(arg)
        Next
        
        Console.WriteLine("Presione cualquier tecla para salir...")
        Console.ReadKey()
    End Sub
End Module

9.2 Aplicaciones Windows Forms

' En el formulario principal
Public Class MainForm
    Private Sub BtnSaludar_Click(sender As Object, e As EventArgs) Handles BtnSaludar.Click
        Dim nombre = TxtNombre.Text
        MessageBox.Show($"Hola, {nombre}!", "Saludo")
    End Sub
    
    Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' Inicialización
        LblFecha.Text = DateTime.Now.ToString("dd/MM/yyyy")
    End Sub
End Class

9.3 Aplicaciones WPF

' XAML
<Window x:Class="MiApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Mi Aplicación" Height="350" Width="525">
    <StackPanel>
        <TextBox x:Name="TxtNombre" Margin="10"/>
        <Button Content="Saludar" Click="BtnSaludar_Click" Margin="10"/>
        <TextBlock x:Name="LblMensaje" Margin="10"/>
    </StackPanel>
</Window>

' Código detrás
Class MainWindow
    Private Sub BtnSaludar_Click(sender As Object, e As RoutedEventArgs)
        LblMensaje.Text = $"Hola, {TxtNombre.Text}!"
    End Sub
End Class

9.4 ASP.NET Web Forms

' Página .aspx
<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>

<!DOCTYPE html>
<html>
<head runat="server">
    <title>Mi Página</title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:TextBox ID="TxtNombre" runat="server"></asp:TextBox>
        <asp:Button ID="BtnSaludar" runat="server" Text="Saludar" OnClick="BtnSaludar_Click" />
        <asp:Label ID="LblMensaje" runat="server"></asp:Label>
    </form>
</body>
</html>

' Código detrás
Partial Class _Default
    Inherits System.Web.UI.Page
    
    Protected Sub BtnSaludar_Click(sender As Object, e As EventArgs)
        LblMensaje.Text = "Hola, " & TxtNombre.Text
    End Sub
End Class

9.5 ASP.NET MVC

' Controlador
Public Class HomeController
    Inherits Controller
    
    Function Index() As ActionResult
        Return View()
    End Function
    
    <HttpPost>
    Function Saludar(nombre As String) As ActionResult
        ViewBag.Mensaje = $"Hola, {nombre}!"
        Return View("Index")
    End Function
End Class

' Vista Index.vbhtml
@ModelType Object
@Code
    ViewData("Title") = "Inicio"
End Code

<h2>Saludo</h2>
@Using Html.BeginForm("Saludar", "Home")
    @Html.TextBox("nombre")
    @<input type="submit" value="Saludar" />
End Using
@If ViewBag.Mensaje IsNot Nothing Then
    @<p>@ViewBag.Mensaje</p>
End If

10. Conectividad y Bases de Datos

10.1 ADO.NET

Imports System.Data.SqlClient

Public Function ObtenerClientes() As DataTable
    Dim tabla As New DataTable
    Dim cadenaConexion = "Server=miServidor;Database=miDB;Integrated Security=True;"
    
    Using conexion As New SqlConnection(cadenaConexion)
        Dim comando As New SqlCommand("SELECT * FROM Clientes", conexion)
        conexion.Open()
        
        Using adaptador As New SqlDataAdapter(comando)
            adaptador.Fill(tabla)
        End Using
    End Using
    
    Return tabla
End Function

' Ejemplo con parámetros
Public Sub InsertarCliente(nombre As String, email As String)
    Dim cadenaConexion = "Server=miServidor;Database=miDB;Integrated Security=True;"
    
    Using conexion As New SqlConnection(cadenaConexion)
        Dim consulta = "INSERT INTO Clientes (Nombre, Email) VALUES (@Nombre, @Email)"
        Dim comando As New SqlCommand(consulta, conexion)
        
        comando.Parameters.AddWithValue("@Nombre", nombre)
        comando.Parameters.AddWithValue("@Email", email)
        
        conexion.Open()
        comando.ExecuteNonQuery()
    End Using
End Sub

10.2 Entity Framework

Imports System.Data.Entity

Public Class MiContexto
    Inherits DbContext
    
    Public Property Clientes As DbSet(Of Cliente)
    Public Property Pedidos As DbSet(Of Pedido)
End Class

Public Class Cliente
    Public Property ClienteId As Integer
    Public Property Nombre As String
    Public Property Email As String
    Public Property Pedidos As ICollection(Of Pedido)
End Class

' Consultas
Using contexto As New MiContexto()
    ' LINQ to Entities
    Dim clientes = From c In contexto.Clientes
                   Where c.Nombre.Contains("a")
                   Order By c.Nombre
                   Select c
    
    ' Métodos de extensión
    Dim cliente = contexto.Clientes.
        Include(Function(c) c.Pedidos).
        FirstOrDefault(Function(c) c.ClienteId = 1)
    
    ' Insertar
    Dim nuevoCliente As New Cliente With {
        .Nombre = "Ana",
        .Email = "ana@example.com"
    }
    contexto.Clientes.Add(nuevoCliente)
    contexto.SaveChanges()
End Using

10.3 Web Services y API REST

' Consumir API REST
Async Function ObtenerDatosAsync() As Task(Of String)
    Using cliente As New HttpClient()
        cliente.BaseAddress = New Uri("https://api.example.com/")
        cliente.DefaultRequestHeaders.Accept.Clear()
        cliente.DefaultRequestHeaders.Accept.Add(
            New MediaTypeWithQualityHeaderValue("application/json"))
        
        Dim respuesta = Await cliente.GetAsync("datos/1")
        If respuesta.IsSuccessStatusCode Then
            Return Await respuesta.Content.ReadAsStringAsync()
        Else
            Throw New Exception($"Error: {respuesta.StatusCode}")
        End If
    End Using
End Function

' Crear Web API
<RoutePrefix("api/clientes")>
Public Class ClientesController
    Inherits ApiController
    
    <HttpGet>
    <Route("")>
    Public Function Get() As IHttpActionResult
        Using contexto As New MiContexto()
            Dim clientes = contexto.Clientes.ToList()
            Return Ok(clientes)
        End Using
    End Function
    
    <HttpPost>
    <Route("")>
    Public Function Post(<FromBody> cliente As Cliente) As IHttpActionResult
        If Not ModelState.IsValid Then
            Return BadRequest(ModelState)
        End If
        
        Using contexto As New MiContexto()
            contexto.Clientes.Add(cliente)
            contexto.SaveChanges()
            Return Created($"api/clientes/{cliente.ClienteId}", cliente)
        End Using
    End Function
End Class

11. Mejores Prácticas y Patrones

11.1 Principios SOLID

11.2 Patrón Repository

Public Interface IRepository(Of T As Class)
    Function GetAll() As IEnumerable(Of T)
    Function GetById(id As Integer) As T
    Sub Add(entity As T)
    Sub Update(entity As T)
    Sub Delete(entity As T)
End Interface

Public Class GenericRepository(Of T As Class)
    Implements IRepository(Of T)
    
    Protected ReadOnly _context As DbContext
    
    Public Sub New(context As DbContext)
        _context = context
    End Sub
    
    Public Function GetAll() As IEnumerable(Of T) Implements IRepository(Of T).GetAll
        Return _context.Set(Of T)().ToList()
    End Function
    
    ' Implementación de otros métodos...
End Class

11.3 Inyección de Dependencias

Public Interface ILogger
    Sub Log(mensaje As String)
End Interface

Public Class ConsoleLogger
    Implements ILogger
    
    Public Sub Log(mensaje As String) Implements ILogger.Log
        Console.WriteLine($"[LOG] {mensaje}")
    End Sub
End Class

Public Class Servicio
    Private ReadOnly _logger As ILogger
    
    Public Sub New(logger As ILogger)
        _logger = logger
    End Sub
    
    Public Sub HacerAlgo()
        _logger.Log("Iniciando operación...")
        ' Lógica del servicio
        _logger.Log("Operación completada")
    End Sub
End Class

' Configuración (ejemplo con Simple Injector)
Dim container As New Container()
container.Register(Of ILogger, ConsoleLogger)()
container.Verify()

' Uso
Dim servicio = container.GetInstance(Of Servicio)()
servicio.HacerAlgo()

11.4 Patrón MVVM (para WPF)

Public Class MainViewModel
    Implements INotifyPropertyChanged
    
    Private _nombre As String
    Public Property Nombre As String
        Get
            Return _nombre
        End Get
        Set(value As String)
            _nombre = value
            OnPropertyChanged(NameOf(Nombre))
            OnPropertyChanged(NameOf(Saludo))
        End Set
    End Property
    
    Public ReadOnly Property Saludo As String
        Get
            Return If(String.IsNullOrEmpty(Nombre), "Hola", $"Hola, {Nombre}!")
        End Get
    End Property
    
    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged
    
    Protected Sub OnPropertyChanged(propertyName As String)
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))
    End Sub
End Class

' XAML
<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>
<StackPanel>
    <TextBox Text="{Binding Nombre, UpdateSourceTrigger=PropertyChanged}"/>
    <TextBlock Text="{Binding Saludo}"/>
</StackPanel>

12. Recursos y Referencias

12.1 Documentación oficial

12.2 Libros recomendados

12.3 Comunidades