Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Un'interfaccia contiene definizioni per un gruppo di funzionalità correlate che devono essere implementate da un class o un struct non astratto. Un'interfaccia può definire static metodi. Un'interfaccia può definire un'implementazione predefinita per i membri. Un'interfaccia non può dichiarare dati di istanza, ad esempio campi, proprietà implementate automaticamente o eventi simili a proprietà.
Usando le interfacce, è possibile, ad esempio, includere il comportamento di più origini in una classe. Tale funzionalità è importante in C# perché il linguaggio non supporta l'ereditarietà multipla delle classi. Inoltre è necessario usare un'interfaccia se si vuole simulare l'ereditarietà per le struct, perché non possono effettivamente ereditare da un'altra struct o classe.
Per definire un'interfaccia, si usa la parola chiave interface come illustrato nell'esempio seguente.
interface IEquatable<T>
{
bool Equals(T obj);
}
Il nome di un'interfaccia deve essere un nome di identificatore C# valido. Per convenzione, i nomi di interfaccia iniziano con una lettera I maiuscola.
Qualsiasi classe o struct che implementa l'interfaccia IEquatable<T> deve contenere una definizione per un metodo Equals che corrisponde alla firma specificata dall'interfaccia. Di conseguenza, è possibile affidarsi a una classe di tipo T che implementa IEquatable<T> per contenere un metodo Equals con cui un'istanza della classe può determinare se sia uguale a un'altra istanza della stessa classe.
La definizione di IEquatable<T> non fornisce un'implementazione per Equals. Una classe o uno struct può implementare più interfacce, ma una classe può ereditare solo da una singola classe.
Per altre informazioni sulle classi astratte, vedere Classi e membri delle classi astratte e sealed.
Le interfacce possono contenere metodi di istanza, proprietà, eventi, indicizzatori o qualsiasi combinazione di questi quattro membri. Le interfacce possono contenere costruttori statici, campi, costanti o operatori. I membri dell'interfaccia che non sono campi possono essere static abstract. Un'interfaccia non può contenere campi di istanza, costruttori di istanze o finalizzatori. I membri dell'interfaccia sono pubblici per impostazione predefinita ed è possibile specificare in modo esplicito i modificatori di accessibilità, ad esempio public, protected, internal, private, protected internal o private protected. Un membro private deve avere un'implementazione predefinita.
Per implementare un membro dell'interfaccia usando l'implementazione implicita, il membro corrispondente della classe di implementazione deve essere pubblico, non statico e avere lo stesso nome e la stessa firma del membro dell'interfaccia. È necessario usare l'implementazione esplicita dell'interfaccia per implementare i membri dell'interfaccia che non devono essere pubblici.
Nota
Quando un'interfaccia dichiara membri statici, un tipo che implementa tale interfaccia potrebbe anche dichiarare membri statici con la stessa firma. Tali membri sono distinti e identificati in modo univoco dal tipo che li dichiara. Il membro statico dichiarato in un tipo non esegue l'override del membro statico dichiarato nell'interfaccia.
Una classe o uno struct che implementa un'interfaccia deve fornire un'implementazione per tutti i membri dichiarati senza un'implementazione predefinita fornita dall'interfaccia. Tuttavia, se una classe di base implementa un'interfaccia, qualsiasi classe derivata dalla classe di base eredita tale implementazione.
Nell'esempio seguente viene illustrata un'implementazione dell'interfaccia IEquatable<T>. La classe di implementazione, Car, deve fornire un'implementazione del metodo Equals.
public class Car : IEquatable<Car>
{
public string? Make { get; set; }
public string? Model { get; set; }
public string? Year { get; set; }
// Implementation of IEquatable<T> interface
public bool Equals(Car? car)
{
return (this.Make, this.Model, this.Year) ==
(car?.Make, car?.Model, car?.Year);
}
}
Le proprietà e gli indicizzatori di una classe possono definire funzioni di accesso aggiuntive per una proprietà o un indicizzatore dichiarato in un'interfaccia. Ad esempio, un'interfaccia può dichiarare una proprietà con una funzione di accesso get. La classe che implementa l'interfaccia può dichiarare la stessa proprietà con una funzione di accesso get o set. Tuttavia, se la proprietà o l'indicizzatore usa l'implementazione esplicita, le funzioni di accesso devono corrispondere. Per altre informazioni sull'implementazione esplicita, vedere Implementazione esplicita dell'interfaccia e Proprietà dell'interfaccia.
Le interfacce possono ereditare da una o più interfacce. L'interfaccia derivata eredita i membri dalle relative interfacce di base. Una classe che implementa un'interfaccia derivata deve implementare tutti i membri nell'interfaccia derivata, inclusi tutti i membri delle interfacce di base dell'interfaccia derivata. Tale classe potrebbe essere convertita in modo implicito nell'interfaccia derivata o in una delle relative interfacce di base. Una classe può includere un'interfaccia più volte tramite le classi di base ereditate o tramite le interfacce ereditate da altre interfacce. Tuttavia, la classe può fornire un'implementazione di un'interfaccia solo una volta e solo se la classe dichiara l'interfaccia durante la definizione della classe (class ClassName : InterfaceName). Se l'interfaccia viene ereditata perché è stata ereditata una classe base che implementa l'interfaccia, la classe base fornisce l'implementazione dei membri dell'interfaccia. Tuttavia, la classe derivata può reimplementare qualsiasi membro dell'interfaccia invece di usare l'implementazione ereditata. Quando le interfacce dichiarano un'implementazione predefinita di un metodo, qualsiasi classe che implementa tale interfaccia eredita tale implementazione (è necessario eseguire il cast dell'istanza della classe al tipo di interfaccia per accedere all'implementazione predefinita nel membro di interfaccia).
Una classe base può implementare anche i membri di interfaccia usando membri virtuali. In tal caso, una classe derivata può modificare il comportamento dell'interfaccia eseguendo l'override dei membri virtuali. Per altre informazioni su membri virtuali, vedere Polimorfismo.
Uso delle interfacce interne
Un'interfaccia interna può in genere essere implementata usando l'implementazione implicita con membri pubblici, purché tutti i tipi nella firma dell'interfaccia siano accessibili pubblicamente. Tuttavia, quando un'interfaccia usa tipi interni nelle firme dei membri, l'implementazione implicita diventa impossibile perché il membro della classe di implementazione deve essere pubblico durante l'esposizione di tipi interni. In questi casi, è necessario usare l'implementazione esplicita dell'interfaccia.
L'esempio seguente illustra entrambi gli scenari:
// Internal type that cannot be exposed publicly
internal class InternalConfiguration
{
public string Setting { get; set; } = "";
}
// Internal interface that CAN be implemented with public members
// because it only uses public types in its signature
internal interface ILoggable
{
void Log(string message); // string is public, so this works with implicit implementation
}
// Interface with internal accessibility using internal types
internal interface IConfigurable
{
void Configure(InternalConfiguration config); // Internal type prevents implicit implementation
}
// This class shows both implicit and explicit interface implementation
public class ServiceImplementation : ILoggable, IConfigurable
{
// Implicit implementation works for ILoggable because string is public
public void Log(string message)
{
Console.WriteLine($"Log: {message}");
}
// Explicit implementation required for IConfigurable because it uses internal types
void IConfigurable.Configure(InternalConfiguration config)
{
// Implementation here
Console.WriteLine($"Configured with: {config.Setting}");
}
// If we tried implicit implementation for IConfigurable, this wouldn't compile:
// public void Configure(InternalConfiguration config) // Error: cannot expose internal type
}
Nell'esempio precedente l'interfaccia IConfigurable usa un tipo InternalConfiguration interno nella firma del metodo. La ServiceImplementation classe non può usare l'implementazione implicita perché ciò richiederebbe rendere pubblico il Configure metodo, che non è consentito quando la firma del metodo contiene tipi interni. Viene invece usata l'implementazione esplicita dell'interfaccia, che non ha un modificatore di accesso ed è accessibile solo tramite il tipo di interfaccia.
Al contrario, l'interfaccia ILoggable può essere implementata in modo implicito con membri pubblici perché tutti i tipi nella relativa firma (string) sono accessibili pubblicamente, anche se l'interfaccia stessa è interna.
Per altre informazioni sull'implementazione esplicita dell'interfaccia, vedere Implementazione esplicita dell'interfaccia.
Riepilogo delle interfacce
Un'interfaccia presenta le proprietà seguenti:
- Nelle versioni C# precedenti alla 8.0, un'interfaccia è simile a una classe base astratta con solo membri astratti. Una classe o uno struct che implementa l'interfaccia deve implementarne tutti i membri.
- A partire da C# 8.0, un'interfaccia può definire implementazioni predefinite per alcuni o tutti i relativi membri. Una classe o uno struct che implementa l'interfaccia non deve implementare membri con implementazioni predefinite. Per ulteriori informazioni, consultare Metodi di interfaccia predefiniti.
- Non è possibile creare direttamente un'istanza di un'interfaccia.
- Una classe o struct può implementare più interfacce. Una classe può ereditare una classe base e anche implementare una o più interfacce.