CodeGym /Cursos /C# SELF /Sobrecarga de métodos (Method Overloading)

Sobrecarga de métodos (Method Overloading)

C# SELF
Nivel 21 , Lección 1
Disponible

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) { ... }
¡No se puede sobrecargar solo por tipo de retorno!

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
WriteLine()
Imprime una línea vacía
WriteLine(string)
Imprime una cadena
WriteLine(int)
Imprime un número entero
WriteLine(double)
Imprime un número decimal
WriteLine(string, object)
Formatea una cadena con un argumento
WriteLine(string, params object[])
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?
greeter.Greet()
void Greet()
greeter.Greet(75)
void Greet(int)

¿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}");
}
Método con número variable de parámetros ( params)

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;
}
Sobrecarga por el modificador ref

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) |
    +-----------------------+
Esquema de sobrecarga de métodos en una clase

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.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION