0Pricing
C# Academy · Lezione

Estensione di interfacce e generics

Aggiunga comportamenti a molti tipi contemporaneamente.

Estensione di interfacce e generics è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 2 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento C# Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso C# Academy include 4 lezioni in totale.

Estendere le interfacce

È possibile scrivere un metodo di estensione il cui parametro this è un'interfaccia. Ogni tipo che implementa l'interfaccia acquisisce immediatamente il metodo. È esattamente il modo in cui LINQ aggiunge decine di metodi a IEnumerable<T>.

Un'estensione generica su IEnumerable

Rendendo il metodo generico rispetto a T ed estendendo IEnumerable<T>, l'helper funziona con qualsiasi sequenza: liste, array, risultati di query e altro ancora.

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());
    }
}

Iterazione della sequenza

All'interno del metodo di estensione, si tratta il parametro come una qualsiasi IEnumerable<T>. Qui si calcola una somma senza fare affidamento su 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());
    }
}

Restituire una nuova sequenza

I metodi di estensione possono restituire anche sequenze. In combinazione con yield return, diventano operatori in stile LINQ a esecuzione differita.

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);
    }
}

Vincoli sui tipi generici

È possibile vincolare il parametro generico per richiedere determinate funzionalità. Qui where T : IComparable<T> consente di confrontare gli elementi per trovare il valore massimo.

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());
    }
}

Estendere un'interfaccia personalizzata

Anche le interfacce definite autonomamente traggono vantaggio da questo approccio. Si definisce un'interfaccia, quindi si aggiunge un comportamento condiviso tramite i metodi di estensione invece di duplicarlo in ogni implementazione.

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());
    }
}

Più parametri di tipo

I metodi di estensione possono essere generici rispetto a più parametri di tipo. Questo helper di proiezione trasforma una sequenza usando una funzione, proprio come Select di 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);
    }
}

Combinare con LINQ integrato

I metodi di estensione coesistono con LINQ. È possibile concatenare il proprio operatore personalizzato con quelli standard in un'unica pipeline fluente.

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));
    }
}

Estendere IEnumerable raggiunge molti tipi

Poiché array, elenchi, dizionari, insiemi hash e risultati di query implementano tutti IEnumerable<T>, un solo metodo di estensione può coprirli tutti contemporaneamente.

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());
    }
}

Perché questo approccio è scalabile

Le estensioni delle interfacce offrono helper polimorfici senza ricorrere all'ereditarietà. Si scrive la logica una sola volta basandosi sull'interfaccia, e ogni implementazione, presente o futura, la ottiene 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(" -> "));
    }
}

Provi personalmente

Realizzi un piccolo operatore generico su IEnumerable<T> e lo concateni con LINQ. Noti come funzioni con qualsiasi tipo di sequenza.

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 rapida

Rifletta su come vengono dichiarati i metodi di estensione in stile LINQ.

Riepilogo

Estendere le interfacce, in particolare IEnumerable<T>, è alla base della programmazione in stile LINQ.

  • Renda il metodo generico e usi l'interfaccia come tipo this.
  • Usi yield return per creare operatori a esecuzione differita.
  • Applichi i vincoli where per richiedere funzionalità come IComparable<T>.
  • Una sola estensione dell'interfaccia copre tutte le implementazioni, comprese quelle future.

Domande Frequenti

La lezione «Estensione di interfacce e generics» è gratuita?

Sì — il testo completo di «Estensione di interfacce e generics» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso C# Academy, passa a CoddyKit PRO. Il corso C# Academy include 4 lezioni in totale.

Cosa imparerò in «Estensione di interfacce e generics»?

Aggiunga comportamenti a molti tipi contemporaneamente. Eserciti C# Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare C# Academy?

Non è richiesta alcuna esperienza precedente. C# Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 2 di 4.

Quanto tempo richiede la lezione «Estensione di interfacce e generics»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione C# Academy?

Sì. Ogni lezione C# Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Definizione dei metodi di estensione
  2. Estensione di interfacce e generics
  3. Risoluzione dei metodi di estensione
  4. Progettazione di buone estensioni
← Torna a C# Academy