1. Introducción
Supón que tenemos una clase Person:
public class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
Y aquí te sientes casi un genio — puedes crear personas con cualquier nombre y edad. Pero hay un problema: ¡a veces no hay suficiente información! El usuario de la app solo metió el nombre, y la edad por alguna razón se sabe después. O quieres en algunos casos poner una edad "estándar" por defecto. Claro, podrías buscarte la vida, pero C# te da una solución elegante para esto — sobrecarga de constructores.
Sobrecarga de constructores significa que una clase puede tener varios constructores con el mismo nombre (el nombre de la clase), pero con diferentes listas de parámetros.
¿En qué se diferencian esas listas? Pueden diferenciarse por:
- Cantidad de parámetros: Por ejemplo, un constructor recibe 2 parámetros, otro — 3.
- Tipos de parámetros: Uno recibe (string, int), otro — (string, decimal).
- Orden de los parámetros: (string, int) y (int, string) — son firmas diferentes.
La regla más importante: el compilador distingue los constructores (y cualquier método) por su firma. La firma incluye el nombre del constructor (o método) y la lista de sus parámetros (cantidad, tipos y orden). Los modificadores de acceso (public, private) y, para métodos normales, el tipo de retorno, no forman parte de la firma. Pero los constructores no tienen tipo de retorno, y el nombre siempre coincide con el de la clase.
2. Sobrecarga de constructores: sintaxis y ejemplo
public class Cat
{
public string Name;
public string Color;
// Constructor sin parámetros
public Cat()
{
Name = "Gato sin nombre";
Color = "Gris";
}
// Constructor con un parámetro
public Cat(string name)
{
Name = name;
Color = "Gris";
}
// Constructor con dos parámetros
public Cat(string name, string color)
{
Name = name;
Color = color;
}
}
Uso:
Cat cat1 = new Cat(); // "Gato sin nombre", "Gris"
Cat cat2 = new Cat("Murzik"); // "Murzik", "Gris"
Cat cat3 = new Cat("Ryzhik", "Naranja"); // "Ryzhik", "Naranja"
Si al crear el objeto pones parámetros, C# "adivina" el constructor correcto por la cantidad y tipo de los mismos.
¡Esto es súper cómodo! El usuario de tu clase no tiene que poner parámetros que no conoce: si solo quiere un gato — usa el constructor sin parámetros, si sabe el nombre — usa otro, y así. Si quiere controlar el color — usa la tercera opción.
3. ¿Cómo funciona la llamada a constructores sobrecargados?
C# elige el constructor correcto automáticamente en tiempo de compilación, según el tipo y cantidad de argumentos que le pases. Es difícil equivocarse — si te lías, el compilador te lo dice clarito.
Vamos a añadir un constructor con un parámetro entero:
public Cat(int age)
{
Name = "Gato sin nombre";
Color = "Gris";
// Código extra para la edad
}
Ahora podemos crear también un "gatito por edad":
Cat oldCat = new Cat(5); // Llama al constructor Cat(int age)
| Llamada al constructor | ¿Cuál se llama? |
|---|---|
|
Sin parámetros |
|
Con un parámetro string |
|
Con dos parámetros string |
|
Con un parámetro int |
4. Llamadas internas de constructores: palabra clave this
A veces, diferentes constructores hacen cosas parecidas (o iguales). Para no copiar código, puedes "llamar a un constructor desde otro". Para eso se usa la palabra clave this.
public class Cat
{
public string Name;
public string Color;
public Cat() : this("Gato sin nombre")
{
// Este constructor llama a Cat(string name)
}
public Cat(string name) : this(name, "Gris")
{
// Este constructor llama a Cat(string name, string color)
}
public Cat(string name, string color)
{
Name = name;
Color = color;
}
}
Al final:
- new Cat() llama a Cat(string name), que llama a Cat(string name, string color).
- Todos los "caminos llevan a Roma": o sea, toda la inicialización está en un solo constructor "principal".
A este enfoque se le llama constructor chaining (encadenamiento de constructores).
5. Ejemplos de la vida real
¡Vamos a crear una clase héroe (Hero) para un juego, que tiene nombre y nivel! Así la escribiríamos:
Con un solo constructor:
public class Hero
{
public string Name;
public int Level;
public Hero(string name, int level)
{
Name = name;
Level = level;
}
}
¡Y ahora vamos a dejar que se cree de varias formas!
Varios constructores:
public class Hero
{
public string Name;
public int Level;
// Si no se indica nada, el héroe será "Sin nombre" de nivel 1
public Hero() : this("Sin nombre", 1)
{
}
// Si solo sabemos el nombre, nivel por defecto — 1
public Hero(string name) : this(name, 1)
{
}
// El constructor principal con dos parámetros
public Hero(string name, int level)
{
Name = name;
Level = level;
}
}
Ahora puedes crear héroes de varias formas:
Hero h1 = new Hero(); // "Sin nombre", 1
Hero h2 = new Hero("Artur"); // "Artur", 1
Hero h3 = new Hero("Lora", 10); // "Lora", 10
6. Detalles de implementación: trampas ocultas y matices
Primero: el compilador no crea un constructor "vacío" por defecto si tú declaras al menos otro constructor. Así que, si escribiste tu constructor con parámetros pero olvidaste añadir el sin parámetros, esta llamada:
Hero h = new Hero(); // ¡Error si no hay constructor sin parámetros!
dará error de compilación.
Segundo: si en el chaining de constructores llamas al que no toca, puedes romper la lógica de inicialización. Es importante ser consistente: mejor que todo el trabajo real esté en el constructor más completo, y los demás solo pasen datos allí con this(...).
Tercero: si los parámetros solo se diferencian por tipo (por ejemplo, Cat(string s) y Cat(object o)), puedes liarla al llamar al constructor con un argumento null. El compilador no siempre sabe cuál quieres usar.
7. Sobrecarga e inicialización de campos
A menudo en una clase hay campos que sí o sí hay que poner. La sobrecarga de constructores ayuda a hacerlo cómodo, pero tú decides qué valores por defecto tienen sentido para tu clase.
Ejemplo con comprobación:
public class Book
{
public string Title;
public int Year;
// Libro por defecto — "Sin título", año 2000
public Book() : this("Sin título", 2000)
{
}
public Book(string title) : this(title, 2000)
{
}
public Book(string title, int year)
{
Title = title;
Year = year;
}
}
8. Sobrecarga con diferentes conjuntos de parámetros
Puedes encontrarte clases con decenas de constructores. No siempre es buena arquitectura (a veces es mejor usar métodos de configuración o el patrón "builder"), pero para objetos de usuario, modelos, DTO — es una opción válida.
Ejemplo: clase "Mascota", donde puedes indicar todas las características, o solo las más necesarias.
public class Pet
{
public string Name;
public int Age;
public string Type;
public bool IsVaccinated;
// Constructor por defecto
public Pet() : this("NoName", 0, "Cat", false) { }
// Mínima información
public Pet(string name, string type) : this(name, 0, type, false) { }
// Constructor completo
public Pet(string name, int age, string type, bool isVaccinated)
{
Name = name;
Age = age;
Type = type;
IsVaccinated = isVaccinated;
}
}
Diferencia entre sobrecarga de método y de constructor
| Método normal | Constructor (incluyendo sobrecargado) |
|---|---|
| Se puede llamar en cualquier momento | Solo se llama al crear el objeto (new) |
| Puedes devolver valores de cualquier tipo | Nunca devuelve valor (no se indica tipo) |
| El nombre del método es cualquiera, normalmente un verbo | El nombre siempre es igual al de la clase |
| Se puede sobrecargar por parámetros | También se sobrecarga por parámetros |
GO TO FULL VERSION