0Pricing
C# Academy · Lezione

Risoluzione dei metodi di estensione

Comprenda come il compilatore individua le estensioni.

Risoluzione dei metodi di estensione è una lezione C# Academy gratuita su CoddyKit. Questa è la lezione 3 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.

Come il compilatore trova i metodi di estensione

Quando si scrive value.Method() e non esiste alcun metodo di istanza corrispondente, il compilatore cerca un metodo di estensione. Tuttavia, prende in considerazione solo le estensioni il cui spazio dei nomi che le contiene è nell'ambito visibile tramite una direttiva using.

Le direttive using rendono visibili i metodi di estensione

Un metodo di estensione è visibile solo se si importa il relativo spazio dei nomi. Senza il using corretto, il metodo sembra non esistere, anche se è compilato nell'assembly.

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

I metodi di istanza vengono verificati per primi

Il compilatore risolve la chiamata in più fasi: prima cerca un metodo di istanza corrispondente, comprese le versioni ereditate. Solo se nessuno è adatto cerca i metodi di estensione.

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

Gli spazi dei nomi più vicini hanno la precedenza

Se due metodi di estensione corrispondono, prevale quello il cui spazio dei nomi è più vicino nella gerarchia di annidamento. Un'ambiguità che il compilatore non riesce a risolvere causa un errore di compilazione e richiede di specificare quale metodo usare.

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

Disambiguare con una chiamata statica

Quando le estensioni entrano in conflitto o si desidera essere espliciti, si può chiamare direttamente il metodo statico usando il nome completo della classe. In questo modo si ignora completamente la risoluzione delle estensioni.

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

Risoluzione degli overload tra i metodi di estensione

Se più metodi di estensione condividono lo stesso nome, la normale risoluzione degli overload sceglie la corrispondenza migliore in base ai tipi dei parametri, proprio come nel normale overload dei metodi.

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

Inferenza generica durante la risoluzione

Per le estensioni generiche, il compilatore deduce gli argomenti di tipo dal ricevitore e dagli argomenti. Se l'inferenza non riesce, l'estensione non viene presa in considerazione.

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

global using e import impliciti

Le versioni moderne di C# consentono di dichiarare direttive global using affinché uno spazio dei nomi sia nell'ambito visibile dell'intero progetto. In questo modo le librerie di estensioni condivise sono disponibili ovunque senza ripetere le importazioni.

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 il tipo del ricevitore è importante

La risoluzione dipende dal tipo statico del ricevitore, non dal suo tipo a runtime. Un'estensione dichiarata per un tipo base si applica anche quando la variabile contiene un'istanza derivata, a meno che non sia nell'ambito visibile un'estensione più specifica.

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

Un modello mentale per la risoluzione

Si consideri questo ordine: (1) i metodi di istanza, poi (2) i metodi di estensione degli spazi dei nomi importati, a partire da quelli più vicini, quindi (3) la risoluzione degli overload tra i candidati. Conoscere quest'ordine spiega quasi ogni risultato sorprendente.

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

Provi personalmente

Definisca un metodo di estensione in uno spazio dei nomi, lo importi e lo chiami. Quindi chiami esplicitamente lo stesso metodo come metodo statico per verificare che entrambi i percorsi funzionino.

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 rapida

Rifletta su ciò che rende visibile un metodo di estensione.

Riepilogo

La risoluzione dei metodi di estensione segue un ordine preciso.

  • I metodi di istanza vengono provati per primi; le estensioni colmano solo le lacune.
  • Un'estensione è visibile solo se il relativo spazio dei nomi è nell'ambito visibile tramite using o global using.
  • Gli spazi dei nomi più vicini hanno la precedenza; i conflitti irrisolti causano errori di compilazione.
  • È sempre possibile chiamare esplicitamente il metodo statico per disambiguare.
  • La risoluzione usa il tipo statico del ricevitore.

Domande Frequenti

La lezione «Risoluzione dei metodi di estensione» è gratuita?

Sì — il testo completo di «Risoluzione dei metodi di estensione» è 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 «Risoluzione dei metodi di estensione»?

Comprenda come il compilatore individua le estensioni. 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 3 di 4.

Quanto tempo richiede la lezione «Risoluzione dei metodi di estensione»?

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