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

Sobrecarga de métodos (Method Overloading)

C# SELF
Nível 21 , Lição 1
Disponível

1. Introdução

Na vida real, várias ações são tipo canivete suíço: o mesmo comando pode funcionar com diferentes ferramentas. Por exemplo, imagina um caixa eletrônico: se você coloca o cartão — ele pede o PIN; se digita o número do celular — ele espera o código do SMS. A ação é a mesma — "verificar usuário", mas os jeitos são diferentes.

Na programação, a gente tromba direto com isso: precisa fazer uma operação só, mas os dados podem ser de tipos diferentes ou com quantidades diferentes de parâmetros. Tipo, nosso método tem que mostrar uma saudação tanto pra uma pessoa quanto pra um animal, ou somar dois, três ou até dez números inteiros.

Claro, dá pra dar nomes diferentes pros métodos: SumTwo, SumThree, SumArray. Mas programador é preguiçoso (não é à toa que dizem que preguiça move o progresso). Fora que o código fica menos legível.

Sobrecarga de método

Sobrecarga de métodos é um jeito de "fazer" o mesmo método funcionar com diferentes conjuntos de parâmetros, sem mudar o nome do método. Isso é uma forma de polimorfismo, mas não tem a ver com herança.

Sobrecarga de método é a possibilidade de criar numa mesma classe (ou struct) vários métodos com o mesmo nome, mas com listas de parâmetros diferentes (tipo, quantidade e/ou ordem).

Assinatura do método

A assinatura do método em C# é o nome dele mais o(s) tipo(s) e ordem dos parâmetros. O tipo de retorno do método não faz parte da assinatura! Isso costuma causar uns bugs chatos (vou falar disso já já).

2. Sobrecarga na prática: exemplos simples

Bora criar uma classe Greeter que vai cumprimentar usuários de jeitos diferentes: só pelo nome, pelo nome e idade, ou até sem parâmetro nenhum.


public class Greeter
{
    // Saudação sem parâmetros
    public void Greet()
    {
        Console.WriteLine("Oi, mundo!");
    }

    // Saudação com nome
    public void Greet(string name)
    {
        Console.WriteLine($"Oi, {name}!");
    }

    // Saudação com nome e idade
    public void Greet(string name, int age)
    {
        Console.WriteLine($"Oi, {name}! Já tem {age} anos? Daora!");
    }
}

Agora dá pra chamar qualquer um desses métodos, e o compilador C# escolhe sozinho a versão certa — olhando a quantidade e os tipos dos parâmetros que você passou.

var greeter = new Greeter();
greeter.Greet();                // Oi, mundo!
greeter.Greet("Anya");          // Oi, Anya!
greeter.Greet("Pyotr", 23);     // Oi, Pyotr! Já tem 23 anos? Daora!

3. Diferença pelo tipo e quantidade de parâmetros

A sobrecarga rola se os métodos diferem por:

  • quantidade de parâmetros,
  • tipo de pelo menos um parâmetro,
  • ordem dos tipos dos parâmetros (mas aqui tem que tomar cuidado).

Vamos tentar adicionar mais uma sobrecarga, que recebe só a idade:


public void Greet(int age)
{
    Console.WriteLine($"Essa idade é massa! ({age} anos)");
}

Agora as chamadas:

greeter.Greet(10);          // Essa idade é massa! (10 anos)

Importante lembrar: se os métodos diferem só pelo tipo de retorno, não dá pra sobrecarregar não rola. Tipo, esse código vai dar erro:


// Erro de compilação!
public int Foo(string s) { ... }
public double Foo(string s) { ... }
Não dá pra sobrecarregar só pelo tipo de retorno!

O compilador vai reclamar: "Já tem método Foo(string), faz algo mais difícil aí!"

4. Sobrecarga e a biblioteca padrão do C#

Sobrecarga não é só nosso Greet inventado. Bora olhar a documentação do .NET pra Console.WriteLine:

Assinatura Pra que serve
WriteLine()
Imprime uma linha vazia
WriteLine(string)
Imprime uma string
WriteLine(int)
Imprime um número inteiro
WriteLine(double)
Imprime um número decimal
WriteLine(string, object)
Formata uma string com um argumento
WriteLine(string, params object[])
Formata com vários argumentos

Tudo isso são sobrecargas do mesmo método — WriteLine. Agora você entende porque sempre dá pra fazer:

Console.WriteLine("Só uma string");
Console.WriteLine(123);
Console.WriteLine(2.5);
Console.WriteLine("Soma: {0}", 42);

E o compilador sempre entende direitinho sua chamada!

5. Como o compilador escolhe qual sobrecarga chamar?

Aqui é na régua: ele olha os tipos e a quantidade dos argumentos que você passou. Uma tabelinha pra clarear:

Chamada Qual versão vai rodar?
greeter.Greet()
void Greet()
greeter.Greet(75)
void Greet(int)

E se rolar ambiguidade?

Às vezes a coisa sai do controle. Exemplo de sobrecarga ambígua — o compilador não consegue escolher a versão certa


public void Print(int a, double b) { ... }
public void Print(double a, int b) { ... }

printer.Print(5, 10); 
// Erro: ambiguidade — qual Print chamar? (os dois servem)

O compilador vai dar erro de ambiguidade. Nesses casos, melhor evitar sobrecargas com a mesma quantidade de parâmetros e tipos parecidos, porque isso confunde o compilador.

6. params — quantidade variável de parâmetros

Digamos que você quer um método que aceita uma quantidade indefinida de números. Aí entra a palavra-chave params.


public void SumAll(params int[] numbers)
{
    int sum = 0;
    foreach (int n in numbers)
        sum += n;
    Console.WriteLine($"Soma: {sum}");
}
Método com número variável de parâmetros ( params)

Agora você pode chamar:

SumAll(1, 2, 3);           // Soma: 6
SumAll(10, 20);            // Soma: 30
SumAll();                  // Soma: 0

Métodos com params podem ser combinados com sobrecarga, mas o principal é não criar sobrecargas que deixem o compilador confuso sobre qual versão você quis chamar.

7. Sobrecarga e modificadores de parâmetro (ref, out, in)

C# diferencia métodos pelos modificadores de parâmetro (ou seja, a assinatura void Foo(int a) é diferente de void Foo(ref int a), e os dois podem existir na mesma classe):

public void SetValue(int a)
{
    a = 42;
}

public void SetValue(ref int a)
{
    a = 100;
}
Sobrecarga pelo modificador ref

Chamar sem ref vai pra primeira versão, com ref — pra segunda:

int n = 5;
SetValue(n);     // n continua 5 (valor copiado)
SetValue(ref n); // n vira 100

8. Esquema: o que é sobrecarga


      +----------+
      |  MyClass |
      +----------+
           |
           |            (trecho 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 na classe

E se for no código:

// Chamando as versões sobrecarregadas do 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. Exemplo: sobrecarregando métodos no nosso app

Bora continuar nosso app de estudo, adicionando sobrecarga de método na hierarquia dos animais.


public class Animal
{
    public string Name { get; set; }

    // Método pra fazer barulho
    public virtual void MakeSound()
    {
        Console.WriteLine("Um som meio estranho...");
    }

    // Método sobrecarregado: som com volume definido
    public void MakeSound(int volume)
    {
        Console.WriteLine($"O animal faz um som com volume {volume} dB.");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Au au!");
    }

    // Método sobrecarregado: latido com volume
    public void MakeSound(int volume)
    {
        Console.WriteLine($"Au au! (volume: {volume} dB)");
    }
}

Tenta essas chamadas:

Dog rex = new Dog();
rex.MakeSound();           // Au au!
rex.MakeSound(75);         // Au au! (volume: 75 dB)

Repara: na classe filha (Dog) a gente sobrecarregou o método MakeSound(int volume), e agora tem as duas versões: com e sem parâmetro.

10. Erros comuns na sobrecarga de métodos

Erro #1: tentar sobrecarregar só pelo tipo de retorno.
Não rola — tipo de retorno não faz parte da assinatura do método. Sobrecarga tem que ser diferente na quantidade ou tipos dos parâmetros de entrada, não pelo void ou int.

Erro #2: sobrecargas ambíguas que confundem o compilador.
Sobrecargas com o mesmo número de parâmetros e tipos parecidos (tipo int e double) podem deixar o compilador perdido. Exemplo: Print(int a, double b) e Print(double a, int b) — chamar Print(1, 1) vai dar erro de ambiguidade.

Erro #3: conflito de params com outras sobrecargas.
Método com params pode capturar uma chamada que era pra outra sobrecarga. Se os tipos batem, o compilador pode escolher o método errado sem você perceber.

Erro #4: esquecer que ref e out fazem parte da assinatura.
Métodos Do(ref int x) e Do(out int x) são sobrecargas diferentes. Se não lembrar disso, é fácil se confundir e chamar a versão errada do método.

2
Tarefa
C# SELF, nível 21, lição 1
Bloqueado
Criação de métodos sobrecarregados para calcular a área de figuras
Criação de métodos sobrecarregados para calcular a área de figuras
2
Tarefa
C# SELF, nível 21, lição 1
Bloqueado
Método para saída formatada de dados
Método para saída formatada de dados
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION