1. Introducción
En la vida real, muchas acciones son como navajas suizas multifunción: el mismo comando puede funcionar con diferentes herramientas. Por ejemplo, imagina un cajero automático: si metes la tarjeta — el cajero pide el pin; si introduces el número de teléfono — el cajero espera el código de confirmación por SMS. La acción es la misma — "verificar usuario", pero las formas son distintas.
En programación nos topamos a menudo con una situación parecida: hay que hacer, en teoría, una sola operación, pero los datos pueden ser de distintos tipos o con diferente cantidad de parámetros. Por ejemplo, nuestro método debería mostrar un saludo tanto para una persona como para un animal, o sumar dos, tres o incluso diez números enteros.
Claro, podríamos llamar a los métodos de forma diferente: SumTwo, SumThree, SumArray. Pero los programadores somos vagos (no por nada dicen que la pereza es el motor del progreso). Además, así el código se vuelve menos legible.
Sobrecarga de método
Sobrecarga de métodos — es una forma de "hacer que" un mismo método funcione con diferentes conjuntos de parámetros, manteniendo el mismo nombre. Es una forma de polimorfismo, pero no relacionada con la herencia.
La sobrecarga de método es la posibilidad de crear en una clase (o estructura) varios métodos con el mismo nombre, pero con diferentes listas de parámetros (por tipo, cantidad y/o orden).
Signatura del método
La signatura de un método en C# — es su nombre más el/los tipo(s) y el orden de los parámetros. ¡El tipo de retorno del método no forma parte de la signatura! Esto a menudo lleva a errores inesperados (de eso te hablo en un momento).
2. Sobrecarga en acción: ejemplos sencillos
Vamos a crear una clase Greeter, que saludará a los usuarios de distintas formas: solo por nombre, por nombre y edad, o incluso sin parámetros.
public class Greeter
{
// Saludo sin parámetros
public void Greet()
{
Console.WriteLine("¡Hola, mundo!");
}
// Saludo con nombre
public void Greet(string name)
{
Console.WriteLine($"¡Hola, {name}!");
}
// Saludo con nombre y edad
public void Greet(string name, int age)
{
Console.WriteLine($"¡Hola, {name}! ¿Ya tienes {age} años? ¡Nada mal!");
}
}
Ahora puedes llamar a cualquiera de estos métodos, y el compilador de C# elegirá la versión correcta — según la cantidad y tipos de parámetros que le pases.
var greeter = new Greeter();
greeter.Greet(); // ¡Hola, mundo!
greeter.Greet("Anya"); // ¡Hola, Anya!
greeter.Greet("Pyotr", 23); // ¡Hola, Pyotr! ¿Ya tienes 23 años? ¡Nada mal!
3. Diferencia por tipo y cantidad de parámetros
La sobrecarga funciona si los métodos se diferencian por:
- cantidad de parámetros,
- tipo de al menos un parámetro,
- orden de los tipos de parámetros (pero aquí hay que tener cuidado).
Vamos a añadir otra sobrecarga que solo reciba la edad:
public void Greet(int age)
{
Console.WriteLine($"¡Tener esa edad mola! ({age} años)");
}
Ahora las llamadas:
greeter.Greet(10); // ¡Tener esa edad mola! (10 años)
Importante recordar: si los métodos solo se diferencian por el tipo de retorno, no se pueden sobrecargar. Por ejemplo, este código dará error:
// ¡Error de compilación!
public int Foo(string s) { ... }
public double Foo(string s) { ... }
El compilador se quejará: "Ya está definido el método Foo(string), ¡ponte creativo!"
4. Sobrecarga y la biblioteca estándar de C#
La sobrecarga no es solo nuestro invento con Greet. Mira la documentación de .NET para Console.WriteLine:
| Signatura | Propósito |
|---|---|
|
Imprime una línea vacía |
|
Imprime una cadena |
|
Imprime un número entero |
|
Imprime un número decimal |
|
Formatea una cadena con un argumento |
|
Formatea con varios argumentos |
Estas son todas sobrecargas del mismo método — WriteLine. Ahora entiendes por qué siempre puedes hacer:
Console.WriteLine("Solo una cadena");
Console.WriteLine(123);
Console.WriteLine(2.5);
Console.WriteLine("Suma: {0}", 42);
¡Y el compilador siempre interpreta tu llamada correctamente!
5. ¿Cómo elige el compilador qué sobrecarga llamar?
Aquí es estricto: mira los tipos y la cantidad de argumentos que realmente le pasas. Una pequeña tabla para verlo claro:
| Llamada | ¿Qué versión se ejecuta? |
|---|---|
|
|
|
|
¿Qué pasa si hay ambigüedad?
A veces la cosa se complica. Un ejemplo de sobrecarga ambigua — el compilador no puede elegir la versión correcta
public void Print(int a, double b) { ... }
public void Print(double a, int b) { ... }
printer.Print(5, 10);
// Error: ambigüedad — ¿qué Print llamar? (ambos parecen válidos)
El compilador dará un error de ambigüedad. En estos casos es mejor evitar sobrecargas con la misma cantidad de parámetros y tipos parecidos, cuando eso puede confundir al compilador.
6. params — cantidad variable de parámetros
Supón que quieres un método que acepte una cantidad indefinida de números. Aquí te ayuda la palabra clave params.
public void SumAll(params int[] numbers)
{
int sum = 0;
foreach (int n in numbers)
sum += n;
Console.WriteLine($"Suma: {sum}");
}
Ahora puedes llamar:
SumAll(1, 2, 3); // Suma: 6
SumAll(10, 20); // Suma: 30
SumAll(); // Suma: 0
Puedes combinar métodos con params y sobrecarga, pero lo importante es no crear sobrecargas que confundan al compilador sobre cuál versión querías usar.
7. Sobrecarga y modificadores de parámetros (ref, out, in)
C# diferencia métodos por los modificadores de parámetros (o sea, la signatura void Foo(int a) es distinta de void Foo(ref int a), y ambos pueden existir en la misma clase):
public void SetValue(int a)
{
a = 42;
}
public void SetValue(ref int a)
{
a = 100;
}
La llamada sin ref irá a la primera versión, con ref — a la segunda:
int n = 5;
SetValue(n); // n sigue siendo 5 (se copia el valor)
SetValue(ref n); // n pasa a ser 100
8. Esquema: qué es la sobrecarga
+----------+
| MyClass |
+----------+
|
| (fragmento de métodos)
+-----------------------+
| void Foo() |
| void Foo(int a) |
| void Foo(string s) |
| void Foo(int a, int b) |
+-----------------------+
Y si lo vemos en código:
// Llamamos a las versiones sobrecargadas del método Foo():
var mc = new MyClass();
mc.Foo(); // void Foo()
mc.Foo(5); // void Foo(int)
mc.Foo("Hello"); // void Foo(string)
mc.Foo(2, 3); // void Foo(int, int)
9. Ejemplo: sobrecargamos métodos en nuestra app
Vamos a seguir mejorando nuestra app de aprendizaje, añadiendo sobrecarga de método en la jerarquía de animales.
public class Animal
{
public string Name { get; set; }
// Método para hacer sonido
public virtual void MakeSound()
{
Console.WriteLine("Algún sonido raro...");
}
// Método sobrecargado: sonido con volumen indicado
public void MakeSound(int volume)
{
Console.WriteLine($"El animal hace un sonido de {volume} dB.");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("¡Guau!");
}
// Método sobrecargado: ladrido con volumen
public void MakeSound(int volume)
{
Console.WriteLine($"¡Guau! (volumen: {volume} dB)");
}
}
Prueba estas llamadas:
Dog rex = new Dog();
rex.MakeSound(); // ¡Guau!
rex.MakeSound(75); // ¡Guau! (volumen: 75 dB)
Ojo: en la clase hija (Dog) hemos sobrecargado el método MakeSound(int volume), y ahora tiene ambas versiones: con y sin parámetro.
10. Errores típicos al sobrecargar métodos
Error nº1: intentar sobrecargar solo por tipo de retorno.
Esto no se puede — el tipo de retorno no forma parte de la signatura del método. La sobrecarga debe diferenciarse por la cantidad o tipos de parámetros de entrada, no por void o int.
Error nº2: sobrecargas ambiguas que confunden al compilador.
Sobrecargas con la misma cantidad de parámetros y tipos parecidos (por ejemplo, int y double) pueden liar al compilador. Ejemplo: Print(int a, double b) y Print(double a, int b) — llamar a Print(1, 1) dará error de ambigüedad.
Error nº3: conflicto de params con otras sobrecargas.
Un método con params puede interceptar una llamada pensada para otra sobrecarga. Si los tipos coinciden, el compilador puede elegir un método distinto al que esperabas.
Error nº4: olvidar que ref y out forman parte de la signatura.
Los métodos Do(ref int x) y Do(out int x) se consideran sobrecargas distintas. Si no tienes esto en cuenta, es fácil confundirse y llamar a la versión equivocada del método.
GO TO FULL VERSION