0Pricing
C# Academy · Aula

Resolução de métodos de extensão

Entenda como o compilador encontra extensões.

Resolução de métodos de extensão é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 4 aulas no total.

Como o compilador encontra as extensões

Quando você escreve value.Method() e nenhum método de instância correspondente existe, o compilador procura um método de extensão. Porém, ele só considera extensões cujo espaço de nomes que as contém esteja no escopo por meio de uma diretiva using.

As diretivas using colocam as extensões no escopo

Uma extensão só fica visível se você importar o espaço de nomes dela. Sem o using correto, parece que o método não existe, embora ele esteja compilado na biblioteca.

using System;
using MyHelpers; // brings StringExtensions into scope

namespace MyHelpers
{
    public static class StringExtensions
    {
        public static string Reverse(this string s)
        {
            var arr = s.ToCharArray();
            Array.Reverse(arr);
            return new string(arr);
        }
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("hello".Reverse());
    }
}

Os métodos de instância são verificados primeiro

O compilador resolve a chamada em etapas: primeiro procura um método de instância correspondente, incluindo os herdados. Só quando nenhum se encaixa é que procura métodos de extensão.

using System;
using System.Collections.Generic;

public static class ListExt
{
    // Ignored: List<int> already has Add(int)
    public static void Add(this List<int> list, int a, int b)
        => list.Add(a + b);
}

public class Program
{
    public static void Main()
    {
        var list = new List<int>();
        list.Add(5); // the built-in instance Add wins for this signature
        Console.WriteLine(list[0]);
    }
}

Espaços de nomes mais próximos têm preferência

Se duas extensões corresponderem, vence aquela cujo espaço de nomes estiver mais próximo na hierarquia de aninhamento. Uma ambiguidade que o compilador não consiga resolver causa um erro de compilação e obriga você a desambiguar.

using System;

namespace Outer
{
    public static class A { public static string Tag(this int n) => "A" + n; }

    namespace Inner
    {
        public static class B { public static string Tag(this int n) => "B" + n; }

        public class Program
        {
            public static void Main()
            {
                // B is in the nearer (Inner) namespace, so it is preferred
                Console.WriteLine(7.Tag());
            }
        }
    }
}

Desambiguando com uma chamada estática

Quando houver conflito entre extensões ou quando você quiser ser explícito, chame o método estático diretamente usando o nome completo da classe. Isso ignora totalmente a resolução de extensões.

using System;

public static class FormatExt
{
    public static string Bracket(this string s) => "[" + s + "]";
}

public class Program
{
    public static void Main()
    {
        // Explicit static call, no ambiguity possible
        Console.WriteLine(FormatExt.Bracket("x"));
    }
}

Resolução de sobrecarga entre extensões

Se vários métodos de extensão tiverem o mesmo nome, a resolução normal de sobrecarga escolherá a melhor correspondência pelos tipos dos parâmetros, assim como na sobrecarga comum de métodos.

using System;

public static class PrintExt
{
    public static string Describe(this int n) => "int:" + n;
    public static string Describe(this string s) => "str:" + s;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(42.Describe());
        Console.WriteLine("hi".Describe());
    }
}

Inferência genérica durante a resolução

Para extensões genéricas, o compilador infere os argumentos de tipo a partir do receptor e dos argumentos. Se a inferência falhar, a extensão não será considerada.

using System;
using System.Collections.Generic;

public static class SeqExt
{
    public static T FirstItem<T>(this IEnumerable<T> source)
    {
        foreach (var item in source) return item;
        throw new InvalidOperationException("empty");
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 5, 6, 7 };
        // T inferred as int from the List<int> receiver
        Console.WriteLine(nums.FirstItem());
    }
}

Diretivas using globais e importações implícitas

O C# moderno pode declarar diretivas global using para que um espaço de nomes fique no escopo de todo o projeto. Isso disponibiliza bibliotecas de extensões compartilhadas em toda parte, sem repetir as importações.

using System;

// In a real project a separate file might say: global using MyHelpers;
namespace MyHelpers
{
    public static class IntExt { public static int Twice(this int n) => n * 2; }
}

public class Program
{
    public static void Main()
    {
        // Works here because MyHelpers is referenced explicitly
        Console.WriteLine(MyHelpers.IntExt.Twice(8));
    }
}

Quando o tipo do receptor é importante

A resolução depende do tipo estático do receptor, não do tipo em tempo de execução. Uma extensão declarada para um tipo base se aplica mesmo quando a variável contém uma instância derivada, a menos que uma extensão mais específica esteja no escopo.

using System;

public class Animal { }
public class Cat : Animal { }

public static class AnimalExt
{
    public static string Kind(this Animal a) => "animal";
}

public class Program
{
    public static void Main()
    {
        Animal a = new Cat();
        // Chosen by static type Animal
        Console.WriteLine(a.Kind());
    }
}

Um modelo mental para a resolução

Pense na ordem assim: (1) métodos de instância, depois (2) métodos de extensão de espaços de nomes importados, começando pelo mais próximo, e então (3) resolução de sobrecarga entre os candidatos. Conhecer essa ordem explica quase todo resultado surpreendente.

using System;

public static class Ext
{
    public static string Wrap(this string s) => "<" + s + ">";
}

public class Program
{
    public static void Main()
    {
        // No instance Wrap exists, so the extension is used
        Console.WriteLine("node".Wrap());
    }
}

Experimente

Defina uma extensão em um espaço de nomes, importe-o e chame-o. Depois, chame o mesmo método explicitamente como um método estático para ver as duas formas funcionando.

using System;
using Text.Helpers;

namespace Text.Helpers
{
    public static class StringExtensions
    {
        public static int Vowels(this string s)
        {
            int count = 0;
            foreach (var c in s.ToLower())
                if ("aeiou".IndexOf(c) >= 0) count++;
            return count;
        }
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine("education".Vowels());
        Console.WriteLine(Text.Helpers.StringExtensions.Vowels("education"));
    }
}

Verificação rápida

Considere o que torna um método de extensão visível.

Recapitulação

A resolução de métodos de extensão segue uma ordem clara.

  • Os métodos de instância são tentados primeiro; as extensões apenas preenchem as lacunas.
  • Uma extensão só fica visível se o espaço de nomes dela estiver no escopo por meio de using ou global using.
  • Os espaços de nomes mais próximos vencem; empates não resolvidos causam erros de compilação.
  • Você sempre pode chamar o método estático explicitamente para desambiguar.
  • A resolução usa o tipo estático do receptor.

Perguntas Frequentes

A aula “Resolução de métodos de extensão” é grátis?

Sim — o texto completo de “Resolução de métodos de extensão” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 4 aulas no total.

O que vou aprender em “Resolução de métodos de extensão”?

Entenda como o compilador encontra extensões. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.

Quanto tempo leva a aula “Resolução de métodos de extensão”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Definindo métodos de extensão
  2. Estendendo interfaces e genéricos
  3. Resolução de métodos de extensão
  4. Projetando boas extensões
← Voltar para C# Academy