Con esto se cierra el Módulo 2: hasta ahora, cada libro de BiblioTechConsola era un puñado de
variables sueltas (un string para el título, otro para el autor, otro para el ISBN, un bool
para la disponibilidad) que había que declarar y mantener sincronizadas a mano, una vez por
cada libro. Si BiblioTech gestionara cien libros, harían falta cuatrocientas variables —o
cuatro arrays paralelos donde la posición i de cada uno tuviera que corresponder al mismo
libro, con el riesgo constante de desincronizarlos. En este Módulo 3: Programación Orientada
a Objetos vas a resolver ese problema de raíz aprendiendo a definir tus propios tipos: las
clases. Una clase agrupa en un único lugar los datos (propiedades) y, más adelante, el
comportamiento (métodos) de un concepto del mundo real —un libro, un socio, un préstamo—, de
modo que cada libro de BiblioTech pase a ser un único objeto autocontenido en lugar de cuatro
variables dispersas. Esta primera lección sienta las bases: qué es una clase, qué es un
objeto, cómo se define una clase con propiedades, y cómo se crean objetos a partir de ella con
new.
Contenido
- De variables sueltas a objetos: por qué las clases
- ¿Qué es una clase? ¿Qué es un objeto?
- Definir una clase con la palabra clave
class - Campos y propiedades (
{ get; set; }) - Creando objetos con
new - Inicializadores de objeto
- Múltiples objetos de la misma clase
- Clases frente a tipos primitivos: tipos por referencia
- De variables sueltas a objetos: por qué las clases
Recupera cómo se representaba un libro en los módulos anteriores:
string tituloLibro1 = "Cien anos de soledad";
string autorLibro1 = "Gabriel Garcia Marquez";
string isbnLibro1 = "978-84-376-0494-7";
bool disponibleLibro1 = true;
string tituloLibro2 = "Rayuela";
string autorLibro2 = "Julio Cortazar";
string isbnLibro2 = "978-84-376-0495-4";
bool disponibleLibro2 = false;Fíjate en el problema: nada en el código indica que tituloLibro1, autorLibro1,
isbnLibro1 y disponibleLibro1 pertenecen al mismo libro; es una convención que solo
existe en la cabeza de quien programa, sostenida a base de nombrar cuidadosamente cada
variable. Añadir un tercer libro significa copiar y pegar cuatro líneas más, con el nombre
Libro3, y confiar en no equivocarse. Este módulo resuelve esto agrupando esos cuatro datos en
un único tipo llamado Libro, de modo que cada libro real de la biblioteca sea representado
por un solo objeto.
- ¿Qué es una clase? ¿Qué es un objeto?
Una clase es una plantilla (o "molde") que describe qué datos y qué comportamiento tendrán los objetos creados a partir de ella. No es, en sí misma, un libro concreto: es la definición de "qué es y qué puede hacer un libro" en el sistema. Un objeto es una instancia concreta de una clase: un libro real, con sus propios valores para el título, el autor, el ISBN y la disponibilidad.
La relación entre clase y objeto es la misma que entre un plano de una casa y las casas construidas con ese plano:
| Concepto | Analogía (plano de una casa) | En BiblioTech |
|---|---|---|
| Clase | El plano: define que toda casa tiene puertas, ventanas, habitaciones | Libro: define que todo libro tiene título, autor, ISBN, disponibilidad |
| Objeto | Una casa concreta, construida siguiendo el plano | Un ejemplar concreto, por ejemplo "Rayuela" |
| Varios objetos | Muchas casas distintas, construidas con el mismo plano | Muchos libros distintos, todos con la misma estructura |
De un mismo plano se pueden construir tantas casas como se quiera, cada una con su propia dirección y sus propios muebles; de una misma clase se pueden crear tantos objetos como se quiera, cada uno con sus propios valores.
- Definir una clase con la palabra clave
class
classUna clase se define con la palabra clave class seguida de un nombre (por convención, en
PascalCase, empezando por mayúscula) y un cuerpo entre llaves { }:
Esto ya es una clase válida, aunque todavía vacía: no describe ningún dato. El siguiente paso es indicar qué información tiene cada libro.
Convención de nombres: las clases en C# se nombran en
PascalCase(Libro,Socio,Prestamo), a diferencia de las variables y parámetros, que usancamelCase(titulo,numeroSocio). Este curso seguirá esta convención de forma estricta a partir de ahora.
- Campos y propiedades (
{ get; set; })
{ get; set; })Dentro de una clase, los datos se pueden representar de dos formas: campos y propiedades.
Un campo es la forma más simple: una variable declarada directamente dentro de la clase.
Sin embargo, en C# lo habitual —y lo recomendado— es usar propiedades, que se declaran de
forma parecida a un campo pero incluyen { get; set; } a continuación:
class Libro
{
public string Titulo { get; set; }
public string Autor { get; set; }
public string Isbn { get; set; }
public bool Disponible { get; set; }
}Esta sintaxis se llama propiedad autoimplementada (auto-implemented property): get
permite leer el valor, y set permite asignarlo, sin que tengas que escribir tú el código que
almacena el dato internamente (el compilador genera ese almacenamiento oculto por ti). A
efectos prácticos, de momento se comportan igual que un campo público, pero con dos ventajas
importantes que irán cobrando sentido a lo largo del módulo:
- Su nombre se escribe en
PascalCase(Titulo, notitulo), siguiendo la convención de C# para todo lo que es visible desde fuera de la clase. - Más adelante (lección de Encapsulamiento) podrás añadir lógica de validación dentro del
setsin cambiar cómo se usa la propiedad desde fuera —algo que un campo público no permite hacer de forma limpia.
Campo (public string titulo;) |
Propiedad (public string Titulo { get; set; }) |
|
|---|---|---|
| Sintaxis | Variable declarada directamente | Añade { get; set; } |
| Convención de nombre | camelCase |
PascalCase |
| Permite añadir validación después sin romper el código que la usa | No, de forma limpia | Sí |
| Uso recomendado en C# para datos expuestos públicamente | No | Sí |
A partir de ahora, Libro (y el resto de clases del curso) se definirán siempre con
propiedades, no con campos públicos sueltos.
Esta será la definición de partida de Libro para todo el módulo:
class Libro
{
public string Titulo { get; set; }
public string Autor { get; set; }
public string Isbn { get; set; }
public bool Disponible { get; set; }
}
- Creando objetos con
new
newDefinir la clase Libro no crea ningún libro todavía: solo describe cómo será cualquier libro.
Para crear un objeto concreto —una instancia— se usa la palabra clave new, seguida del
nombre de la clase y paréntesis:
Libro libro1 = new Libro();
libro1.Titulo = "Cien anos de soledad";
libro1.Autor = "Gabriel Garcia Marquez";
libro1.Isbn = "978-84-376-0494-7";
libro1.Disponible = true;
Console.WriteLine(libro1.Titulo); // Cien anos de soledad
Console.WriteLine(libro1.Disponible); // TrueAnalicemos esta línea clave: Libro libro1 = new Libro();
Libro(antes del nombre de variable) indica el tipo de la variablelibro1: será un objeto de tipoLibro.new Libro()crea un objeto nuevo en memoria, siguiendo la plantilla de la claseLibro, y devuelve una referencia a él.libro1es la variable que guarda esa referencia, y a partir de ahí se usa el operador.(punto) para acceder a sus propiedades:libro1.Titulo,libro1.Autor, etc.
Cada propiedad de un objeto recién creado con new Libro() toma su valor por defecto hasta
que se le asigna uno explícitamente: null para string (por eso hay que asignarlo antes de
usarlo, o el programa fallará al leerlo), false para bool, 0 para tipos numéricos como
int.
- Inicializadores de objeto
Asignar cada propiedad en una línea separada, como en el ejemplo anterior, funciona pero es
repetitivo. C# ofrece una alternativa más compacta llamada inicializador de objeto, que
asigna varias propiedades entre llaves { } justo después de new Libro():
Libro libro2 = new Libro
{
Titulo = "Rayuela",
Autor = "Julio Cortazar",
Isbn = "978-84-376-0495-4",
Disponible = false
};
Console.WriteLine(libro2.Titulo); // RayuelaEste código es equivalente a crear el objeto con new Libro() y luego asignar cada propiedad
por separado, pero resulta más legible cuando se inicializan varias propiedades a la vez, ya
que agrupa visualmente "estos valores pertenecen a este objeto".
- Múltiples objetos de la misma clase
La ventaja frente a las variables sueltas del Módulo 1 se aprecia mejor con varios libros a la
vez: cada objeto Libro es completamente independiente de los demás, aunque comparta la misma
estructura definida por la clase.
Libro libro1 = new Libro { Titulo = "Cien anos de soledad", Autor = "Gabriel Garcia Marquez", Isbn = "978-84-376-0494-7", Disponible = true };
Libro libro2 = new Libro { Titulo = "Rayuela", Autor = "Julio Cortazar", Isbn = "978-84-376-0495-4", Disponible = false };
Libro libro3 = new Libro { Titulo = "Ficciones", Autor = "Jorge Luis Borges", Isbn = "978-84-376-0496-1", Disponible = true };
Console.WriteLine($"{libro1.Titulo} - Disponible: {libro1.Disponible}");
Console.WriteLine($"{libro2.Titulo} - Disponible: {libro2.Disponible}");
Console.WriteLine($"{libro3.Titulo} - Disponible: {libro3.Disponible}");Cada uno de los tres objetos (libro1, libro2, libro3) tiene sus propios valores para
Titulo, Autor, Isbn y Disponible, sin ninguna posibilidad de confundir el título de uno
con la disponibilidad de otro: toda la información de un libro concreto vive dentro de su
propio objeto. Modificar libro2.Disponible no afecta en absoluto a libro1 ni a libro3.
- Clases frente a tipos primitivos: tipos por referencia
Una diferencia importante entre un Libro y un tipo primitivo como int o bool es que las
clases son tipos por referencia: una variable de tipo Libro no contiene el objeto en sí,
sino una referencia (una especie de "dirección") que apunta a él en memoria. Esto tiene una
consecuencia práctica que conviene ver desde el principio:
Libro original = new Libro { Titulo = "Rayuela", Disponible = true };
Libro copia = original; // copia no es un libro nuevo: apunta al MISMO objeto que original
copia.Disponible = false;
Console.WriteLine(original.Disponible); // False -- ¡tambien cambio el original!Como copia y original apuntan al mismo objeto en memoria, modificar copia.Disponible
también se refleja al leer original.Disponible: no son dos libros independientes, son dos
nombres distintos para el mismo libro. Esto contrasta con los tipos primitivos (int,
bool, double...), que son tipos por valor: al asignar int b = a;, b recibe una
copia independiente del valor de a. Retomaremos esta distinción con más detalle en la última
lección del módulo, cuando comparemos clases con struct.
Errores Comunes y Consejos
- Olvidar
new: escribirLibro libro1;declara una variable capaz de referenciar unLibro, pero no crea ningún objeto; intentar usarlibro1.Tituloantes de asignarlenew Libro()provoca un error de compilación (o, en ciertos contextos, una excepción en tiempo de ejecución si la variable valenull). - Confundir la clase con el objeto:
Libroes la plantilla;libro1,libro2,libro3son objetos concretos creados a partir de ella. Es un error habitual en las primeras lecciones hablar de "la claselibro1" cuando en realidad es un objeto. - Usar campos públicos sueltos por costumbre: en C# se prefieren las propiedades
(
{ get; set; }) a los campos públicos, precisamente porque permiten añadir validación más adelante sin romper el código que ya las usa. Acostúmbrate a esta sintaxis desde ya. - No caer en que las clases son tipos por referencia: como se ha visto en el último apartado, copiar una variable de tipo clase no duplica el objeto; ambas variables comparten el mismo objeto en memoria. Este detalle explica comportamientos que, de otro modo, parecerían errores extraños.
- Consejo: nombra las clases siempre en singular y en
PascalCase(Libro, noLibrosnilibro): una clase describe un único concepto, aunque luego crees muchos objetos de ella.
Ejercicios
-
Define una clase
Librocon las propiedadesTitulo(string),Autor(string),Isbn(string) yDisponible(bool), todas ellas como propiedades autoimplementadas ({ get; set; }). -
Usando la clase del ejercicio anterior, crea un objeto
LibrollamadomiLibroasignando sus propiedades una a una (sin inicializador de objeto) con los datos: título "El Aleph", autor "Jorge Luis Borges", ISBN "978-84-376-0497-8" y disponibletrue. Muestra por consola el título y la disponibilidad. -
Crea dos objetos
Librodistintos usando el inicializador de objeto (new Libro { ... }), con datos diferentes para cada uno, y muestra por consola el título de cada uno seguido de su disponibilidad, usando interpolación de cadenas.
Soluciones
class Libro
{
public string Titulo { get; set; }
public string Autor { get; set; }
public string Isbn { get; set; }
public bool Disponible { get; set; }
}
Libro miLibro = new Libro();
miLibro.Titulo = "El Aleph";
miLibro.Autor = "Jorge Luis Borges";
miLibro.Isbn = "978-84-376-0497-8";
miLibro.Disponible = true;
Console.WriteLine(miLibro.Titulo);
Console.WriteLine(miLibro.Disponible);
Se crea el objeto vacío con new Libro() y después se asigna cada propiedad por separado;
al final se muestran el título ("El Aleph") y la disponibilidad (True).
Libro libroA = new Libro { Titulo = "Ficciones", Autor = "Jorge Luis Borges", Isbn = "978-84-376-0496-1", Disponible = true };
Libro libroB = new Libro { Titulo = "Rayuela", Autor = "Julio Cortazar", Isbn = "978-84-376-0495-4", Disponible = false };
Console.WriteLine($"{libroA.Titulo} - Disponible: {libroA.Disponible}");
Console.WriteLine($"{libroB.Titulo} - Disponible: {libroB.Disponible}");
Cada objeto se crea e inicializa en un solo paso gracias al inicializador de objeto; al mostrarlos por consola se comprueba que cada uno mantiene sus propios valores de forma independiente.
Conclusión
En esta lección has dado el paso más importante del módulo: has aprendido qué es una clase,
qué es un objeto, y cómo definir tu primera clase propia, Libro, con propiedades
autoimplementadas (Titulo, Autor, Isbn, Disponible). Ya sabes crear objetos con new,
asignarles valores propiedad a propiedad o mediante inicializadores de objeto, y por qué las
clases son tipos por referencia. Con esto, BiblioTechConsola empieza a dejar atrás las
variables sueltas: cada libro es ahora un objeto autocontenido.
Todavía falta una pieza importante: por ahora, un objeto Libro es solo un contenedor de
datos, sin ningún comportamiento propio —para prestarlo o mostrarlo por pantalla seguimos
necesitando código externo a la clase. En la próxima lección, Métodos, aprenderás a añadir
comportamiento directamente a Libro (como Prestar(), Devolver() o MostrarFicha()), de
modo que cada objeto sepa hacer cosas por sí mismo, no solo almacenar datos.
Curso de Programación en C#
Módulo 1: Introducción a C#
- Introducción a C#
- Configuración del Entorno de Desarrollo
- Programa Hola Mundo
- Sintaxis y Estructura Básica
- Variables y Tipos de Datos
- Arrays y Cadenas de Texto
Módulo 2: Estructuras de Control
Módulo 3: Programación Orientada a Objetos
- Clases y Objetos
- Métodos
- Constructores y Destructores
- Herencia
- Polimorfismo
- Encapsulamiento
- Abstracción
- Structs y Records: Tipos por Valor y por Referencia
Módulo 4: Conceptos Avanzados de C#
- Interfaces
- Delegados y Eventos
- Pattern Matching y Características Modernas de C#
- Genéricos
- Colecciones
- LINQ (Consulta Integrada en el Lenguaje)
- Programación Asíncrona
Módulo 5: Trabajando con Datos
- Entrada/Salida de Archivos
- Serialización
- Conectividad con Bases de Datos
- Entity Framework
- Trabajo con JSON y Consumo de APIs REST
Módulo 6: Temas Avanzados
- Reflexión
- Atributos
- Programación Dinámica
- Gestión de Memoria y Recolección de Basura
- Multihilo y Programación Paralela
Módulo 7: Construcción de Aplicaciones
- Formularios de Windows
- WPF (Windows Presentation Foundation)
- ASP.NET Core
- Blazor
- Xamarin y .NET MAUI
Módulo 8: Mejores Prácticas y Patrones de Diseño
- Estándares de Codificación y Mejores Prácticas
- Patrones de Diseño
- Inyección de Dependencias e Inversión de Control
- Pruebas Unitarias
- Revisión y Refactorización de Código
