CodeGym /Corsi /C# SELF /Membri statici nelle interfacce

Membri statici nelle interfacce

C# SELF
Livello 24 , Lezione 1
Disponibile

1. Introduzione

Una volta, le interfacce erano rigide come il regolamento di una scuola privata: solo firme, niente campi, nessuna implementazione, nessun membro statico! Ma .NET si evolve, e il linguaggio di programmazione è come un organismo vivente: per rispondere alle nuove sfide, deve evolversi.

Con l'arrivo delle nuove versioni di C#, le interfacce hanno imparato nuovi trick. Uno dei più evidenti è proprio la possibilità di avere membri statici nelle interfacce. Ora le interfacce possono contenere metodi statici, proprietà e anche eventi. Nelle versioni più recenti di C# (da C# 11 in poi) puoi addirittura dichiarare metodi static abstract, che richiedono l'implementazione nei tipi che implementano quell'interfaccia.

Questa è una svolta enorme nella programmazione, che cambia l'approccio sia alla programmazione generica che a quella orientata agli oggetti.

Per dirla semplice: un membro statico di un'interfaccia è un "membro comune", a cui accedi tramite il tipo stesso dell'interfaccia (o tramite il tipo che la implementa), non tramite un'istanza dell'oggetto.

Fino a poco tempo fa solo classi, struct ed enum potevano avere metodi e proprietà statiche, ma ora questa possibilità ce l'hanno anche le interfacce.

Come si presenta? Esempio di sintassi


public interface IMyMath
{
    static int Add(int a, int b) => a + b; // Metodo statico (implementazione di default)

    static abstract int Multiply(int a, int b); // Da implementare nel tipo che implementa l'interfaccia
}
  • static — il membro è accessibile a livello di tipo, non di istanza.
  • static abstract — il contratto "richiede" di implementare il metodo statico nel tipo che implementa.
  • Nelle interfacce (come nelle classi) ora puoi dichiarare metodi, proprietà ed eventi statici. Puoi anche creare costanti. Tuttavia, le interfacce non possono ancora avere campi di istanza o campi statici (tranne le costanti).

La funzione dei membri statici nelle interfacce è quella di permettere di dichiarare "operazioni" universali. Per esempio, se hai una collezione di oggetti e vuoi chiamare su di loro una "comparazione" senza sapere che tipo implementa l'interfaccia, ora puoi farlo grazie ai membri static abstract.

2. Metodi statici con implementazione nell'interfaccia

Ma a cosa serve davvero?

Problema classico: vuoi descrivere non solo metodi "di istanza" (tipo fare qualcosa con l'oggetto), ma anche "statici" (tipo creare un nuovo oggetto da una stringa o confrontare due oggetti in modo statico). Prima dovevi risolverlo con pattern (Factory, Comparer, Helper), ma ora puoi esprimerlo direttamente nell'interfaccia.

Questo è diventato super importante per i Generics e per gli algoritmi che lavorano con tipi arbitrari:

  • Implementazione di operatori universali (tipo somma, confronto).
  • Vincoli per codice generico: "Qualsiasi tipo che abbia un metodo statico o un operatore…"
  • Serializzazione/deserializzazione: quando devi creare un oggetto da una stringa senza sapere il tipo a compile time.

Metodi statici con corpo

Con C# 8 hanno permesso metodi statici con corpo nelle interfacce. Sono simili ai metodi statici normali nelle classi.


public interface IUtility
{
    static void PrintHello()
    {
        Console.WriteLine("Ciao dall'Interfaccia!");
    }
}

Puoi chiamare questo metodo così: IUtility.PrintHello();

Comodo per funzioni di supporto che hanno senso nell'interfaccia ma non sono legate a una specifica implementazione. Tipo: statistiche su tutti gli oggetti di quel tipo, metodi factory (CreateDefault), controlli comuni (tipo validazione di valori).

Particolarità: i membri statici dell'interfaccia non vengono "sovrascritti" nelle classi

Se dichiari in un'interfaccia static void Method() { ... }, la classe che implementa può dichiarare un metodo statico con la stessa firma — ma non è override! Sono solo due metodi indipendenti — stesso nome, ma non è un "metodo statico virtuale".

3. Membri static abstract

Da C# 11 puoi dichiarare in un'interfaccia metodi static abstract. Significa: "ogni classe o struct che implementa questa interfaccia deve dichiarare un membro statico con la stessa firma".

Esempio:


public interface IParsable<T>
{
    static abstract T Parse(string s);
}

Qualsiasi tipo che implementa questa interfaccia deve dichiarare il metodo statico Parse(string s).

Implementazione di questa interfaccia in una classe


public class Temperature : IParsable<Temperature>
{
    public int Value { get; set; }

    // Implementazione statica!
    public static Temperature Parse(string s)
    {
        var temp = new Temperature();
        temp.Value = int.Parse(s);
        return temp;
    }
}

Come funziona?

Diventa super interessante nel codice generico (Generics):


public static T ParseFromString<T>(string s) where T : IParsable<T>
{
    return T.Parse(s);
}

// Uso:
var temp = ParseFromString<Temperature>("42");

Ora puoi scrivere codice davvero universale che funziona con qualsiasi tipo che implementa un comportamento "statico"!

4. Membri statici nelle interfacce vs. membri statici normali delle classi

Caratteristica Membro statico di classe Membro statico di interfaccia
Ereditato No No, ma viene implementato come parte del contratto dell'interfaccia
Richiede implementazione No Solo se static abstract
Usato nei Generics No (fino a C# 11) Sì (con static abstract)
Puo' avere implementazione di default
Override No No, solo implementazione obbligatoria
Visibilità alla chiamata Tramite nome del tipo Tramite tipo dell'interfaccia o tipo che implementa

7. Esempi dal mondo reale

Vediamo come migliorare una piccola app didattica usando queste nuove possibilità.

Supponiamo di avere l'interfaccia "IPrintable":


public interface IPrintable
{
    void Print();
    static void PrintAll(IEnumerable<IPrintable> items)
    {
        foreach (var item in items)
        {
            item.Print();
        }
    }
}

Ora puoi comodamente chiamare:


var documents = new List<IPrintable>
{
    new Invoice { Number = "INV-001" },
    new Receipt { Number = "RC-007" }
};
IPrintable.PrintAll(documents); // metodo statico dell'interfaccia!

Questa architettura è perfetta per operazioni "di gruppo" su tutte le implementazioni dell'interfaccia.

Esempio più avanzato: somma generica di tipi numerici

Supponiamo di avere un'interfaccia:


public interface IAddable<T>
{
    static abstract T Add(T left, T right);
}

Implementazione per interi (struct wrapper):


public struct MyInt : IAddable<MyInt>
{
    public int Value { get; }
    public MyInt(int val) => Value = val;
    public static MyInt Add(MyInt left, MyInt right) => new MyInt(left.Value + right.Value);
}

E infine, una funzione universale per sommare due numeri di tipo T:


public static T Sum<T>(T a, T b) where T : IAddable<T>
{
    return T.Add(a, b);
}

// Usiamo:
var x = new MyInt(5);
var y = new MyInt(6);
var z = Sum(x, y); // z.Value == 11

È proprio per questa universalità che è stato introdotto il supporto ai membri statici nelle interfacce!

8. Errori tipici e particolarità

La vita con le nuove feature non è sempre semplice come negli esempi. Ecco alcune cose che possono mandare in crisi chi è alle prime armi:

I metodi statici dell'interfaccia non vengono "ereditati" dalla classe che implementa. Se dichiari in un'interfaccia static void Foo(), allora MyClass.Foo() e IMyInterface.Foo() sono due metodi completamente diversi.

Static abstract è obbligatorio da implementare. Se ti dimentichi — il compilatore ti dirà che la classe non implementa completamente l'interfaccia.

Generic constraints: per usare membri static abstract, devi mettere il vincolo sull'interfaccia nei parametri generici (where T : IMyInterface).

Non tutti gli strumenti supportano già queste novità. Per esempio, Rider, VS Code o vecchi analyzer Roslyn non sempre mostrano correttamente i membri static abstract nelle interfacce se la versione di .NET non supporta C# 11+.

Non confondere con i metodi di estensione delle interfacce: quelli si implementano a parte e non funzionano come membri statici.

Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION