0Pricing
C# Academy · Aula

Estendendo interfaces e genéricos

Adicione comportamento a vários tipos de uma só vez.

Estendendo interfaces e genéricos é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 2 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.

Estendendo interfaces

Você pode escrever um método de extensão cujo parâmetro this seja uma interface. Todo tipo que implementa a interface passa imediatamente a ter esse método. É exatamente assim que o LINQ adiciona dezenas de métodos a IEnumerable<T>.

Uma extensão genérica em IEnumerable

Ao tornar o método genérico sobre T e estender IEnumerable<T>, o auxiliar funciona com qualquer sequência: listas, vetores, resultados de consultas e muito mais.

using System;
using System.Collections.Generic;

public static class EnumerableExtensions
{
    public static bool IsEmpty<T>(this IEnumerable<T> source)
    {
        foreach (var item in source) return false;
        return true;
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 1, 2, 3 };
        Console.WriteLine(nums.IsEmpty());
        Console.WriteLine(new int[0].IsEmpty());
    }
}

Iterando a sequência

Dentro da extensão, trate o parâmetro como qualquer IEnumerable<T>. Aqui, calculamos uma soma sem depender do LINQ.

using System;
using System.Collections.Generic;

public static class EnumerableExtensions
{
    public static int SumValues(this IEnumerable<int> source)
    {
        int total = 0;
        foreach (var n in source) total += n;
        return total;
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 10, 20, 30 };
        Console.WriteLine(nums.SumValues());
    }
}

Retornando uma nova sequência

As extensões também podem retornar sequências. Combinadas com yield return, elas se tornam operadores no estilo do LINQ com avaliação tardia.

using System;
using System.Collections.Generic;

public static class EnumerableExtensions
{
    public static IEnumerable<T> EveryOther<T>(this IEnumerable<T> source)
    {
        bool take = true;
        foreach (var item in source)
        {
            if (take) yield return item;
            take = !take;
        }
    }
}

public class Program
{
    public static void Main()
    {
        var letters = new List<string> { "a", "b", "c", "d", "e" };
        foreach (var l in letters.EveryOther())
            Console.WriteLine(l);
    }
}

Restrições de tipos genéricos

Você pode restringir o parâmetro genérico para exigir determinados recursos. Aqui, where T : IComparable<T> permite comparar itens para encontrar um máximo.

using System;
using System.Collections.Generic;

public static class EnumerableExtensions
{
    public static T MaxItem<T>(this IEnumerable<T> source) where T : IComparable<T>
    {
        bool started = false;
        T best = default;
        foreach (var item in source)
        {
            if (!started || item.CompareTo(best) > 0) { best = item; started = true; }
        }
        return best;
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 3, 9, 1, 7 };
        Console.WriteLine(nums.MaxItem());
    }
}

Estendendo uma interface personalizada

Suas próprias interfaces também se beneficiam. Defina uma interface e anexe comportamentos compartilhados por meio de extensões, em vez de duplicá-los em cada implementação.

using System;

public interface INamed { string Name { get; } }

public class Dog : INamed { public string Name => "Rex"; }

public static class NamedExtensions
{
    public static string Greeting(this INamed n) => "Hello, " + n.Name;
}

public class Program
{
    public static void Main()
    {
        var d = new Dog();
        Console.WriteLine(d.Greeting());
    }
}

Vários parâmetros de tipo

As extensões podem ser genéricas em relação a vários parâmetros de tipo. Este auxiliar de projeção transforma uma sequência usando uma função, de forma muito parecida com o Select do LINQ.

using System;
using System.Collections.Generic;

public static class EnumerableExtensions
{
    public static IEnumerable<TResult> Map<TSource, TResult>(
        this IEnumerable<TSource> source, Func<TSource, TResult> f)
    {
        foreach (var item in source) yield return f(item);
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 1, 2, 3 };
        foreach (var s in nums.Map(n => "#" + n))
            Console.WriteLine(s);
    }
}

Combinando com o LINQ integrado

Seus métodos de extensão coexistem com o LINQ. Você pode encadear seu operador personalizado com operadores padrão em um único fluxo encadeado.

using System;
using System.Collections.Generic;
using System.Linq;

public static class EnumerableExtensions
{
    public static IEnumerable<T> EveryOther<T>(this IEnumerable<T> source)
    {
        bool take = true;
        foreach (var item in source) { if (take) yield return item; take = !take; }
    }
}

public class Program
{
    public static void Main()
    {
        var nums = Enumerable.Range(1, 10);
        var result = nums.Where(n => n > 2).EveryOther().ToList();
        Console.WriteLine(string.Join(", ", result));
    }
}

Estender IEnumerable alcança muitos tipos

Como vetores, listas, dicionários, conjuntos de dispersão e resultados de consultas implementam IEnumerable<T>, uma única extensão abrange todos eles de uma só vez.

using System;
using System.Collections.Generic;

public static class EnumerableExtensions
{
    public static int CountItems<T>(this IEnumerable<T> source)
    {
        int n = 0;
        foreach (var item in source) n++;
        return n;
    }
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new[] { 1, 2, 3 }.CountItems());
        Console.WriteLine(new HashSet<string> { "a", "b" }.CountItems());
    }
}

Por que este padrão é escalável

As extensões de interfaces fornecem auxiliares polimórficos sem herança. Escreva a lógica uma vez usando a interface, e toda implementação, atual ou futura, a obterá automaticamente.

using System;
using System.Collections.Generic;

public static class EnumerableExtensions
{
    public static string JoinWith<T>(this IEnumerable<T> source, string sep)
    {
        return string.Join(sep, source);
    }
}

public class Program
{
    public static void Main()
    {
        var nums = new List<int> { 1, 2, 3 };
        Console.WriteLine(nums.JoinWith(" -> "));
    }
}

Experimente

Crie um pequeno operador genérico em IEnumerable<T> e encadeie-o com o LINQ. Observe como ele funciona com qualquer tipo de sequência.

using System;
using System.Collections.Generic;
using System.Linq;

public static class EnumerableExtensions
{
    public static IEnumerable<T> WithoutDuplicatesByKey<T, TKey>(
        this IEnumerable<T> source, Func<T, TKey> keySelector)
    {
        var seen = new HashSet<TKey>();
        foreach (var item in source)
            if (seen.Add(keySelector(item))) yield return item;
    }
}

public class Program
{
    public static void Main()
    {
        var words = new[] { "apple", "avocado", "banana", "cherry", "blueberry" };
        var firstPerLetter = words.WithoutDuplicatesByKey(w => w[0]);
        Console.WriteLine(string.Join(", ", firstPerLetter));
    }
}

Verificação rápida

Pense em como as extensões no estilo do LINQ são declaradas.

Recapitulação

Estender interfaces, especialmente IEnumerable<T>, é a base da programação no estilo do LINQ.

  • Torne o método genérico e use a interface como o tipo this.
  • Use yield return para criar operadores com avaliação tardia.
  • Aplique restrições where para exigir recursos como IComparable<T>.
  • Uma única extensão de interface abrange todas as implementações, inclusive as futuras.

Perguntas Frequentes

A aula “Estendendo interfaces e genéricos” é grátis?

Sim — o texto completo de “Estendendo interfaces e genéricos” é 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 “Estendendo interfaces e genéricos”?

Adicione comportamento a vários tipos de uma só vez. 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 2 de 4.

Quanto tempo leva a aula “Estendendo interfaces e genéricos”?

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