En la lección anterior definiste la clase Libro con sus propiedades (Titulo, Autor, Isbn, Disponible), pero un objeto Libro era, de momento, un contenedor pasivo de datos: para prestarlo había que escribir código externo que pusiera Disponible a false a mano, y para mostrar su ficha había que escribir el Console.WriteLine correspondiente cada vez, en cualquier parte del programa que lo necesitara. Esta lección resuelve eso enseñándote a definir métodos: bloques de código asociados a una clase que representan las acciones que sus objetos pueden realizar. Al final de la lección, un objeto Libro sabrá prestarse, devolverse y mostrar su propia ficha, sin que el código que lo usa tenga que conocer los detalles internos de cómo lo hace.

Contenido

  1. Qué es un método y por qué vive dentro de la clase
  2. Sintaxis de un método: parámetros y tipo de retorno
  3. Métodos void: acciones sin valor de retorno
  4. Añadiendo métodos a Libro: Prestar(), Devolver(), MostrarFicha()
  5. Sobrecarga de métodos
  6. Paso de parámetros por valor
  7. Paso de parámetros por referencia: ref y out
  8. Parámetros opcionales y argumentos nombrados
  9. Métodos de instancia frente a métodos static

  1. Qué es un método y por qué vive dentro de la clase

Un método es una función definida dentro de una clase que describe una acción que sus objetos pueden ejecutar. La diferencia con las funciones locales que ya usaste en el Módulo 2 (como PrestarLibro(string titulo), que recibía el título como parámetro) es que un método vive dentro de la clase y, normalmente, opera directamente sobre las propiedades del propio objeto, sin necesidad de que se las pasen como parámetros.

class Libro
{
    public string Titulo { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; }

    public void Prestar()
    {
        Disponible = false;
    }
}

Fíjate en la diferencia de enfoque: en el Módulo 2, PrestarLibro(tituloLibros, disponibilidad, posicion) necesitaba que le pasaras el array de disponibilidad entero como parámetro. Ahora, libro1.Prestar() no necesita ningún parámetro: el método ya sabe a qué objeto pertenece (a libro1) y accede directamente a su propiedad Disponible. Esta capacidad de un método de acceder a las propiedades del objeto sobre el que se invoca es la base de la programación orientada a objetos.

  1. Sintaxis de un método: parámetros y tipo de retorno

La forma general de un método es:

<modificador de acceso> <tipo de retorno> <NombreMetodo>(<parametros>)
{
    // cuerpo del método
}
  • El modificador de acceso (public, private...) controla desde dónde se puede llamar al método; se estudia en profundidad en la lección de Encapsulamiento. De momento, usaremos public para que sea accesible desde cualquier parte del programa.
  • El tipo de retorno indica qué tipo de dato devuelve el método al terminar (int, string, bool...), o void si no devuelve ningún valor.
  • Los parámetros, entre paréntesis, son los datos que el método necesita recibir para hacer su trabajo (pueden ser cero, uno o varios).
public bool TieneAutor(string autorBuscado)
{
    return Autor == autorBuscado;
}

Este método devuelve un bool: true si el Autor del libro coincide con el parámetro autorBuscado, false en caso contrario. La palabra clave return termina la ejecución del método y entrega el valor indicado a quien lo llamó.

Libro libro1 = new Libro { Titulo = "Rayuela", Autor = "Julio Cortazar" };
bool esDeCortazar = libro1.TieneAutor("Julio Cortazar");
Console.WriteLine(esDeCortazar); // True

  1. Métodos void: acciones sin valor de retorno

Cuando un método realiza una acción pero no necesita devolver ningún dato, su tipo de retorno es void (literalmente, "vacío"). Ya usaste void en las funciones locales del Módulo 2; en un método de clase se comporta igual:

public void MostrarFicha()
{
    Console.WriteLine($"Titulo: {Titulo}");
    Console.WriteLine($"Autor: {Autor}");
    Console.WriteLine($"ISBN: {Isbn}");
    Console.WriteLine($"Disponible: {Disponible}");
}

Un método void puede usar return; (sin ningún valor detrás) para terminar antes de tiempo si hace falta, pero no puede usar return valor;, ya que no tiene ningún tipo al que devolver ese valor.

  1. Añadiendo métodos a Libro: Prestar(), Devolver(), MostrarFicha()

Con lo visto hasta ahora, ya puedes completar la clase Libro con sus tres métodos principales, que acompañarán al objeto durante el resto del módulo:

class Libro
{
    public string Titulo { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; }

    public void Prestar()
    {
        if (Disponible)
        {
            Disponible = false;
            Console.WriteLine($"'{Titulo}' ha sido prestado.");
        }
        else
        {
            Console.WriteLine($"'{Titulo}' no esta disponible para prestamo.");
        }
    }

    public void Devolver()
    {
        Disponible = true;
        Console.WriteLine($"'{Titulo}' ha sido devuelto.");
    }

    public void MostrarFicha()
    {
        Console.WriteLine($"Titulo: {Titulo}");
        Console.WriteLine($"Autor: {Autor}");
        Console.WriteLine($"ISBN: {Isbn}");
        Console.WriteLine($"Disponible: {Disponible}");
    }
}

Y así se usan sobre un objeto concreto:

Libro libro1 = new Libro { Titulo = "Cien anos de soledad", Autor = "Gabriel Garcia Marquez", Isbn = "978-84-376-0494-7", Disponible = true };

libro1.MostrarFicha();
libro1.Prestar();  // 'Cien anos de soledad' ha sido prestado.
libro1.Prestar();  // 'Cien anos de soledad' no esta disponible para prestamo.
libro1.Devolver(); // 'Cien anos de soledad' ha sido devuelto.

Observa la diferencia con el Módulo 2: libro1.Prestar() no necesita que le pases ni el título, ni el array de disponibilidad, ni ninguna posición; el propio objeto ya conoce sus datos y decide, según su estado interno (Disponible), qué hacer. Esta es la mejora concreta que aporta la orientación a objetos frente a las variables sueltas del Módulo 2.

  1. Sobrecarga de métodos

La sobrecarga (overloading) consiste en definir varios métodos con el mismo nombre pero distinta lista de parámetros (distinto número de parámetros, o distinto tipo). C# decide automáticamente cuál de las versiones ejecutar según los argumentos con los que se le llame.

public void MostrarFicha()
{
    Console.WriteLine($"Titulo: {Titulo}");
    Console.WriteLine($"Autor: {Autor}");
    Console.WriteLine($"ISBN: {Isbn}");
    Console.WriteLine($"Disponible: {Disponible}");
}

public void MostrarFicha(bool incluirIsbn)
{
    Console.WriteLine($"Titulo: {Titulo}");
    Console.WriteLine($"Autor: {Autor}");

    if (incluirIsbn)
    {
        Console.WriteLine($"ISBN: {Isbn}");
    }

    Console.WriteLine($"Disponible: {Disponible}");
}
libro1.MostrarFicha();       // usa la version sin parametros
libro1.MostrarFicha(false);  // usa la version con un bool, sin mostrar el ISBN

C# distingue estas dos versiones únicamente por su firma (nombre + número y tipos de parámetros), no por el tipo de retorno ni por los nombres de los parámetros. Dos métodos con el mismo nombre y exactamente los mismos parámetros —aunque tuvieran tipos de retorno distintos— no serían una sobrecarga válida y el compilador lo rechazaría.

Método Parámetros ¿Cuándo se usa?
MostrarFicha() Ninguno Ficha completa por defecto
MostrarFicha(bool incluirIsbn) Un bool Se puede decidir si mostrar o no el ISBN

  1. Paso de parámetros por valor

Por defecto, en C# los parámetros se pasan por valor: el método recibe una copia del valor que se le pasa, no el original. Si el parámetro es de un tipo primitivo (int, bool, double...), modificar el parámetro dentro del método no afecta a la variable original que se pasó al llamarlo.

void Duplicar(int numero)
{
    numero = numero * 2;
    Console.WriteLine($"Dentro del metodo: {numero}");
}

int copiasDisponibles = 5;
Duplicar(copiasDisponibles);
Console.WriteLine($"Fuera del metodo: {copiasDisponibles}"); // sigue siendo 5

Aunque dentro de Duplicar la variable local numero pasa a valer 10, la variable original copiasDisponibles, fuera del método, no se ve afectada: numero era solo una copia.

Nota: esto es distinto de lo que viste en la lección anterior con objetos como Libro. Un parámetro de tipo Libro también se pasa "por valor", pero lo que se copia es la referencia al objeto, no el objeto en sí; por eso, si un método recibe un Libro como parámetro y modifica una de sus propiedades (libro.Disponible = false;), ese cambio se refleja fuera del método, porque la copia de la referencia sigue apuntando al mismo objeto. Volveremos sobre esta diferencia entre tipos por valor y por referencia en la última lección del módulo.

  1. Paso de parámetros por referencia: ref y out

Cuando sí necesitas que un método modifique directamente la variable original (no una copia), C# ofrece dos palabras clave: ref y out.

ref: modificar una variable ya inicializada

ref indica que el parámetro se pasa por referencia: el método recibe acceso directo a la variable original, no a una copia, así que cualquier cambio dentro del método se refleja fuera. La variable debe estar inicializada antes de la llamada, y hay que repetir ref tanto al declarar el método como al llamarlo.

void DuplicarEjemplares(ref int cantidadEjemplares)
{
    cantidadEjemplares = cantidadEjemplares * 2;
}

int ejemplaresRayuela = 3;
DuplicarEjemplares(ref ejemplaresRayuela);
Console.WriteLine(ejemplaresRayuela); // 6 -- esta vez SI cambio

out: un método que "devuelve" varios valores

out se usa para que un método pueda entregar un valor adicional además de (o en lugar de) su return, típicamente para indicar si una operación tuvo éxito junto con un mensaje o resultado. A diferencia de ref, la variable pasada como out no necesita estar inicializada antes de la llamada —el método está obligado a asignarle un valor antes de terminar. Ya usaste este patrón, sin saberlo todavía, con int.TryParse(entrada, out int numero) en el Módulo 1.

bool TryPrestar(Libro libro, out string mensaje)
{
    if (libro.Disponible)
    {
        libro.Disponible = false;
        mensaje = $"'{libro.Titulo}' prestado correctamente.";
        return true;
    }

    mensaje = $"'{libro.Titulo}' no esta disponible.";
    return false;
}

Libro libro2 = new Libro { Titulo = "Ficciones", Disponible = true };

bool exito = TryPrestar(libro2, out string resultado);
Console.WriteLine(resultado); // 'Ficciones' prestado correctamente.
Console.WriteLine(exito);     // True
ref out
La variable debe estar inicializada antes de llamar No
El método está obligado a asignarle un valor No
Uso típico Modificar un valor existente Devolver un resultado adicional (a menudo junto a un bool de éxito)

  1. Parámetros opcionales y argumentos nombrados

Un parámetro opcional tiene un valor por defecto asignado en la propia definición del método; si quien llama al método no proporciona ese argumento, se usa el valor por defecto. Los parámetros opcionales deben ir siempre después de los parámetros obligatorios.

public void RegistrarEjemplar(string titulo, string autor, string estado = "Nuevo")
{
    Console.WriteLine($"Registrado: {titulo} ({autor}) - Estado: {estado}");
}
libro1.RegistrarEjemplar("Rayuela", "Julio Cortazar");            // usa "Nuevo" por defecto
libro1.RegistrarEjemplar("Rayuela", "Julio Cortazar", "Usado");   // sobreescribe el valor por defecto

Los argumentos nombrados permiten indicar explícitamente a qué parámetro corresponde cada valor, escribiendo nombreParametro: valor, en lugar de depender únicamente del orden. Esto es especialmente útil cuando un método tiene varios parámetros opcionales y solo quieres especificar alguno de ellos:

libro1.RegistrarEjemplar(titulo: "Ficciones", autor: "Jorge Luis Borges", estado: "Usado");

// Con argumentos nombrados, el orden deja de importar:
libro1.RegistrarEjemplar(autor: "Jorge Luis Borges", titulo: "Ficciones");

En la segunda llamada, aunque autor se escribe antes que titulo, C# asigna cada valor al parámetro correcto gracias al nombre explícito, y estado toma su valor por defecto ("Nuevo") al no indicarse.

  1. Métodos de instancia frente a métodos static

Todos los métodos vistos hasta ahora son métodos de instancia: solo tiene sentido llamarlos sobre un objeto concreto (libro1.Prestar()), porque operan sobre los datos de ese objeto en particular. Un método static, en cambio, pertenece a la propia clase, no a ningún objeto concreto, y se invoca escribiendo el nombre de la clase en lugar del de una variable:

class Libro
{
    public string Titulo { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; }

    public static bool EsIsbnValido(string isbn)
    {
        string isbnLimpio = isbn.Replace("-", "");
        return isbnLimpio.Length == 13 && isbnLimpio.StartsWith("978") || isbnLimpio.StartsWith("979");
    }

    // Prestar(), Devolver(), MostrarFicha() se mantienen como en el apartado 4
}
bool valido = Libro.EsIsbnValido("978-84-376-0494-7");
Console.WriteLine(valido); // True

Fíjate en que EsIsbnValido se invoca como Libro.EsIsbnValido(...), usando el nombre de la clase, no sobre un objeto (libro1.EsIsbnValido(...) sería un error de compilación). Esto tiene sentido porque validar el formato de un ISBN no depende de ningún libro concreto: es una utilidad general relacionada con el concepto Libro, pero que no necesita ningún objeto para ejecutarse. Por esta misma razón, un método static no puede acceder directamente a propiedades de instancia como Titulo o Disponible (no sabría de qué objeto hablar); recibiría un error de compilación si lo intentara.

Método de instancia Método static
Se invoca sobre Un objeto (libro1.Prestar()) La clase (Libro.EsIsbnValido(...))
Accede a propiedades del objeto (Titulo, Disponible...) No, directamente
Ejemplo en Libro Prestar(), Devolver(), MostrarFicha() EsIsbnValido(string isbn)
Cuándo usarlo La operación depende de los datos de un objeto concreto La operación es una utilidad general, no ligada a ningún objeto

Console.WriteLine y int.Parse, que ya has usado en módulos anteriores, son también métodos static (de las clases Console e int respectivamente): por eso se llaman con el nombre de la clase (Console, no una variable), y no sobre ningún objeto.

Errores Comunes y Consejos

  • Olvidar ref/out al llamar al método: si el método se declaró con ref u out, hay que repetir la palabra clave también en la llamada (DuplicarEjemplares(ref ejemplaresRayuela)), no solo en la definición; si no, el compilador da un error.
  • Usar una variable out sin inicializarla y esperar leerla antes de la llamada: una variable declarada como out string mensaje (inline, en la propia llamada) no tiene ningún valor útil hasta que el método termina de ejecutarse; no intentes leerla antes.
  • Confundir sobrecarga con parámetros opcionales: ambos permiten "llamar al método de varias formas", pero son mecanismos distintos: la sobrecarga define métodos completamente separados (con cuerpos potencialmente distintos), mientras que un parámetro opcional forma parte de un único método con un valor por defecto.
  • Intentar acceder a Titulo o Disponible desde un método static: un método static no está asociado a ningún objeto, así que no puede usar directamente propiedades de instancia; si necesita datos de un libro concreto, ese libro debe llegarle como parámetro.
  • Abusar de ref/out: son útiles en casos concretos (como TryParse/TryPrestar), pero un método que devuelve su resultado con return suele ser más claro que uno que lo hace mediante parámetros ref/out; resérvalos para cuando de verdad aporten algo, no como costumbre.

Ejercicios

  1. Añade a la clase Libro (con las propiedades Titulo, Autor, Isbn, Disponible) un método de instancia bool EsDelAutor(string autorBuscado) que devuelva true si el Autor del libro coincide exactamente con autorBuscado, y false en caso contrario. Pruébalo con un objeto Libro y dos búsquedas distintas.

  2. Escribe un método static bool EsIsbnValido(string isbn) dentro de la clase Libro que devuelva true si, tras quitar los guiones con Replace("-", ""), la cadena resultante tiene exactamente 13 caracteres, y false en caso contrario. Llámalo con Libro.EsIsbnValido(...) usando dos ISBN de ejemplo (uno válido y otro no).

  3. Escribe un método void RegistrarPrestamo(string nombreSocio, int diasPrestamo = 15) que muestre por consola un mensaje con el nombre del socio y los días de préstamo. Llámalo tres veces: una sin indicar diasPrestamo (debe usar 15 por defecto), otra indicando 30 días por posición, y otra usando argumentos nombrados en orden invertido.

Soluciones

class Libro
{
    public string Titulo { get; set; }
    public string Autor { get; set; }
    public string Isbn { get; set; }
    public bool Disponible { get; set; }

    public bool EsDelAutor(string autorBuscado)
    {
        return Autor == autorBuscado;
    }
}

Libro libro1 = new Libro { Titulo = "Rayuela", Autor = "Julio Cortazar" };
Console.WriteLine(libro1.EsDelAutor("Julio Cortazar")); // True
Console.WriteLine(libro1.EsDelAutor("Jorge Luis Borges")); // False
public static bool EsIsbnValido(string isbn)
{
    string isbnLimpio = isbn.Replace("-", "");
    return isbnLimpio.Length == 13;
}

Console.WriteLine(Libro.EsIsbnValido("978-84-376-0494-7")); // True (13 caracteres sin guiones)
Console.WriteLine(Libro.EsIsbnValido("123"));                // False
void RegistrarPrestamo(string nombreSocio, int diasPrestamo = 15)
{
    Console.WriteLine($"{nombreSocio}: prestamo de {diasPrestamo} dias.");
}

RegistrarPrestamo("Ana Martinez");                       // usa 15 dias por defecto
RegistrarPrestamo("Luis Gomez", 30);                      // 30 dias por posicion
RegistrarPrestamo(diasPrestamo: 7, nombreSocio: "Eva Ruiz"); // argumentos nombrados, orden invertido

Las tres llamadas son válidas: la primera usa el valor por defecto de diasPrestamo, la segunda lo indica por posición, y la tercera usa argumentos nombrados, lo que permite escribirlos en cualquier orden.

Conclusión

En esta lección, Libro ha pasado de ser un simple contenedor de datos a un objeto con comportamiento propio: sabe prestarse (Prestar()), devolverse (Devolver()) y mostrar su ficha (MostrarFicha()), y has aprendido las herramientas fundamentales para definir métodos en C#: parámetros y retorno, void, sobrecarga, paso por valor frente a ref/out, parámetros opcionales y nombrados, y la diferencia entre métodos de instancia y métodos static.

Todavía queda una pregunta pendiente: ¿cómo se asegura que un objeto Libro nace siempre con sus propiedades bien inicializadas (por ejemplo, Disponible = true al crear un libro nuevo), en lugar de confiar en que quien lo crea recuerde asignar cada propiedad a mano? La respuesta son los constructores, que verás en la próxima lección junto con la nueva clase Socio: el segundo pilar del modelo de dominio de BiblioTech.

Curso de Programación en C#

Módulo 1: Introducción a C#

Módulo 2: Estructuras de Control

Módulo 3: Programación Orientada a Objetos

Módulo 4: Conceptos Avanzados de C#

Módulo 5: Trabajando con Datos

Módulo 6: Temas Avanzados

Módulo 7: Construcción de Aplicaciones

Módulo 8: Mejores Prácticas y Patrones de Diseño

Módulo 9: Proyecto Final

© Copyright 2026. Todos los derechos reservados