C#: Método Avançado

1. Sobrecarga de métodos

A sobrecarga de métodos permite definir vários métodos com o mesmo nome na mesma classe, desde que suas listas de parâmetros sejam diferentes. Uma lista de parâmetros diferente significa um número, tipo ou ordem de parâmetros diferente. O tipo de retorno não influencia na resolução da sobrecarga.

▶ Exemplo

CSHARP
using System;

class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public double Add(double a, double b)
    {
        return a + b;
    }

    public int Add(int a, int b, int c)
    {
        return a + b + c;
    }
}

class Program
{
    static void Main()
    {
        Calculator calc = new Calculator();
        Console.WriteLine(calc.Add(1, 2));
        Console.WriteLine(calc.Add(1.5, 2.5));
        Console.WriteLine(calc.Add(1, 2, 3));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
3
4
6
💡 Dica: Um tipo de retorno diferente, por si só, não constitui uma sobrecarga — o compilador irá reportar um erro.


2. Parâmetros opcionais e nomeados

Os parâmetros opcionais atribuem valores padrão aos parâmetros na declaração do método; esses parâmetros podem ser omitidos ao chamar o método. Os parâmetros nomeados passam valores especificando os nomes dos parâmetros, sem a necessidade de seguir a ordem posicional.

▶ Exemplo

CSHARP
using System;

class Program
{
    static void Print(string msg, int count = 1, string prefix = "")
    {
        for (int i = 0; i < count; i++)
        {
            Console.WriteLine($"{prefix}{msg}");
        }
    }

    static void Main()
    {
        Print("Hello");
        Print("Hi", 3);
        Print(msg: "World", prefix: "> ");
        Print(prefix: ">> ", count: 2, msg: "Test");
    }
}
▶ Experimente
TEXT 📖 Somente leitura
Hello
Hi
Hi
Hi
> World
>> Test
>> Test
⚠️ Observação: Os parâmetros opcionais devem ser colocados após todos os parâmetros obrigatórios.


3. O modificador de parâmetro ref

A palavra-chave ref passa um parâmetro por referência — as modificações feitas no parâmetro dentro do método afetam a variável do chamador. A variável deve ser inicializada antes da chamada, e a palavra-chave ref também deve ser usada no local da chamada.

▶ Exemplo

CSHARP
using System;

class Program
{
    static void Swap(ref int a, ref int b)
    {
        int temp = a;
        a = b;
        b = temp;
    }

    static void Main()
    {
        int x = 10;
        int y = 20;
        Console.WriteLine($"Before: x={x}, y={y}");
        Swap(ref x, ref y);
        Console.WriteLine($"After: x={x}, y={y}");
    }
}
▶ Experimente
TEXT 📖 Somente leitura
Before: x=10, y=20
After: x=20, y=10

4. O modificador de parâmetro “out”

A palavra-chave out também é passada por referência, mas o método deve atribuir um valor ao parâmetro out, e a variável não precisa ser inicializada antes da chamada. É comumente usada quando um método precisa retornar vários valores.

▶ Exemplo

CSHARP
using System;

class Program
{
    static bool TryParseInt(string s, out int result)
    {
        result = 0;
        foreach (char c in s)
        {
            if (c < '0' || c > '9')
                return false;
            result = result * 10 + (c - '0');
        }
        return true;
    }

    static void Main()
    {
        if (TryParseInt("123", out int number))
        {
            Console.WriteLine($"Parsed: {number}");
        }

        if (!TryParseInt("abc", out int fail))
        {
            Console.WriteLine("Parse failed");
        }
    }
}
▶ Experimente
TEXT 📖 Somente leitura
Parsed: 123
Parse failed
📌 Ponto-chave: os parâmetros out devem ser atribuídos antes que o método retorne. A partir do C# 7, é possível declarar variáveis diretamente no local da chamada.


5. O modificador de parâmetro “in”

A palavra-chave in passa um parâmetro por referência como somente leitura — o método não pode modificar o valor de um parâmetro in. Isso é útil para passar estruturas grandes, a fim de evitar a sobrecarga da cópia e, ao mesmo tempo, impedir modificações acidentais.

▶ Exemplo

CSHARP
using System;

struct BigData
{
    public long A, B, C, D, E, F, G, H;
}

class Program
{
    static void Process(in BigData data)
    {
        Console.WriteLine($"A={data.A}, B={data.B}");
    }

    static void Main()
    {
        BigData d = new BigData { A = 100, B = 200 };
        Process(in d);
        Process(d);
    }
}
▶ Experimente
TEXT 📖 Somente leitura
A=100, B=200
A=100, B=200

ℹ️ Observação: A palavra-chave in pode ser omitida no local da chamada; o compilador passará automaticamente por referência.


6. Argumentos variáveis params

A palavra-chave params permite que um método aceite um número variável de argumentos — o compilador os agrupa em uma matriz. Um método só pode ter um parâmetro params, e este deve ser o último na lista de parâmetros.

▶ Exemplo

CSHARP
using System;

class Program
{
    static int Sum(params int[] numbers)
    {
        int total = 0;
        foreach (int n in numbers)
        {
            total += n;
        }
        return total;
    }

    static void Main()
    {
        Console.WriteLine(Sum(1, 2, 3));
        Console.WriteLine(Sum(10, 20, 30, 40, 50));
        Console.WriteLine(Sum());
    }
}
▶ Experimente
TEXT 📖 Somente leitura
6
150
0
⚠️ Observação: params só pode ser usado com matrizes unidimensionais, e um método pode ter, no máximo, um parâmetro params.


7. Princípios da recursão e exemplos clássicos

A recursão é uma técnica de programação na qual um método chama a si mesmo. Toda recursão deve ter uma condição de terminação (caso base); caso contrário, causará um estouro de pilha.

(1) Fatorial

CSHARP
using System;

class Program
{
    static long Factorial(int n)
    {
        if (n <= 1)
            return 1;
        return n * Factorial(n - 1);
    }

    static void Main()
    {
        for (int i = 1; i <= 10; i++)
        {
            Console.WriteLine($"{i}! = {Factorial(i)}");
        }
    }
}
TEXT 📖 Somente leitura
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
7! = 5040
8! = 40320
9! = 362880
10! = 3628800

(2) Sequência de Fibonacci

CSHARP
using System;

class Program
{
    static int Fibonacci(int n)
    {
        if (n <= 0) return 0;
        if (n == 1) return 1;
        return Fibonacci(n - 1) + Fibonacci(n - 2);
    }

    static void Main()
    {
        for (int i = 0; i <= 10; i++)
        {
            Console.Write($"{Fibonacci(i)} ");
        }
        Console.WriteLine();
    }
}
TEXT 📖 Somente leitura
0 1 1 2 3 5 8 13 21 34 55
🔥 Aviso sobre desempenho: A recursão ingênua para a sequência de Fibonacci envolve muitos cálculos redundantes, com complexidade temporal O(2^n). Em ambiente de produção, use iteração ou memoização para otimizar.


8. Funções locais

O C# 7 introduziu as funções locais, permitindo que métodos sejam definidos dentro de outros métodos. As funções locais só podem ser acessadas dentro do método que as contém e são úteis para extrair lógica auxiliar utilizada apenas em um método específico.

▶ Exemplo

CSHARP
using System;

class Program
{
    static int[] GetEvenSquares(int[] numbers)
    {
        bool IsEven(int n) => n % 2 == 0;
        int Square(int n) => n * n;

        System.Collections.Generic.List```<int>``` result = new System.Collections.Generic.List```<int>```();
        foreach (int n in numbers)
        {
            if (IsEven(n))
            {
                result.Add(Square(n));
            }
        }
        return result.ToArray();
    }

    static void Main()
    {
        int[] data = { 1, 2, 3, 4, 5, 6, 7, 8 };
        int[] result = GetEvenSquares(data);
        Console.WriteLine(string.Join(", ", result));
    }
}
▶ Experimente
TEXT 📖 Somente leitura
4, 16, 36, 64
💡 Dica: As funções locais podem capturar variáveis locais do método que as contém e apresentam menor sobrecarga do que as expressões lambda.


9. Âmbito das variáveis

O escopo de uma variável determina onde ela pode ser acessada. Em C#, existem três níveis principais de escopo: escopo de bloco (dentro de blocos if/for/while), escopo de método (variáveis locais definidas dentro de um método) e escopo de classe (campos definidos em uma classe).

▶ Exemplo

CSHARP
using System;

class ScopeDemo
{
    int classField = 100;

    void MethodA()
    {
        int localVar = 10;
        Console.WriteLine($"classField={classField}, localVar={localVar}");

        if (true)
        {
            int blockVar = 20;
            Console.WriteLine($"blockVar={blockVar}");
        }
    }

    void MethodB()
    {
        Console.WriteLine($"classField={classField}");
    }
}

class Program
{
    static void Main()
    {
        ScopeDemo demo = new ScopeDemo();
        demo.MethodA();
        demo.MethodB();
    }
}
▶ Experimente
TEXT 📖 Somente leitura
classField=100, localVar=10
blockVar=20
classField=100

Regra: Não é permitido declarar variáveis locais com o mesmo nome no mesmo método. Os blocos de código internos podem acessar variáveis externas, mas os blocos externos não podem acessar variáveis de blocos internos.

❓ Perguntas Frequentes

P: A sobrecarga de métodos pode se basear exclusivamente no tipo de retorno? R: Não, o compilador faz a correspondência entre métodos com base nos argumentos no local da chamada; o tipo de retorno não faz parte da assinatura. P: Qual é a principal diferença entre ref e out? R: ref exige inicialização antes da chamada; out exige atribuição dentro do método. ref passa um valor existente que pode ser modificado; out é normalmente usado para retornar resultados adicionais. P: Os argumentos de params podem ser null? R: Sim, params é essencialmente um array; passar null não cria automaticamente um array vazio, portanto, é necessário verificar se há null dentro do método. P: A recursão é sempre mais lenta do que a iteração? R: Não necessariamente, mas a recursão tem limites de profundidade da pilha e sobrecarga de chamadas; uma recursão profunda pode levar a uma exceção StackOverflowException. P: Qual é a diferença entre funções locais e expressões lambda? R: As funções locais são métodos propriamente ditos, sem a sobrecarga dos delegados, e suportam genéricos e ref/out; as expressões lambda geram delegados ou árvores de expressão com alocações adicionais.

📖 Resumo

📝 Exercícios

  1. Escreva três métodos sobrecarregados de Multiply que aceitem, respectivamente, dois inteiros, dois números de precisão dupla e três inteiros, retornando o produto.
  2. Escreva um método void Log(string msg, string level = "INFO", bool timestamp = true) e chame-o pelo menos três vezes, utilizando parâmetros nomeados em ordens diferentes.
  3. Escreva um método void Increment(ref int value) que incremente em 1 o número inteiro passado e verifique, na função Main, se a variável foi de fato modificada.
  4. Escreva um método bool TryDivide(int a, int b, out double result) que retorne false quando b for 0; caso contrário, retorne o resultado da divisão por meio de out.
  5. Escreva um método double Average(params double[] values) que calcule a média de todos os argumentos, tratando o caso de um array vazio.
  6. Implemente um método recursivo para calcular 1+2+3+...+n e compare o resultado com uma versão iterativa.
  7. Escreva um método que utilize internamente uma função local para determinar se um número é primo e que retorne todos os números primos de 1 a 50.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%