Utilizzo delle interfacce in C# |
|
Interface (Interfaccia) è un contenitore con un nome di abstract members (membri astratti) che definiscono dei comportamenti che una certa classeo struttura decide di supportare. I metodi astratti non sono altro che protocolli che non forniscono una implementazione standard, ma che necessitano di essere implementati ogni volta che vengono associati ad una classe derivata.
L'interfaccia polimorfica definita da una classe padre abstract soffre di una limitazione importante in quanto solo i tipi derivati supportano i membri definiti dal padre abstract.
Un altro limite della classe base abstract è che ogni tipo derivato deve contenere i membri astratti e fornirne una loro implementazione, mentre un tipo interface non ha questa limitazione e permette di implementare un comportamento specifico quando necessario.
|
Un'Interface (Interfaccia) viene definita utilizzando la parola chiave interface. Diversamente dalle class, le interface non specificano mai la classe base, inoltre i membri di un'interface non specificano mai un access modifier - come tutte le interfacce, i membri sono implicitamente public e abstract.
// Questa interfaccia definisce il comportamento di "avere dei punti"
public interface IPointy
{
// Implicitamente publico ed astratto
byte GetNumberOfPoints();
}
Ricordare che quando si definiscono i membri di un'interface, non si deve definirne le implementazioni. Le interface sono puri protocolli e pertanto non definiscono alcuna implementazione!!
// interfaccia modificata per avere errori
public interface IPointy
{
// ERRORE: interface non possono avere campi data!!!
public int numberOfPoints;
// ERRORE: interface non hanno costruttori!!!
public IPointy() { numberOfPoints = 0; }
// ERRORE: interface non hanno implementazioni di membri
byte GetNumberOfPoints() { return numberOfPoints; }
// GIUSTO: inteface possono avere delle proprietà prototype
byte Point { get; }
}
Le inteface possono avere eventi.
|
Le Interface (Interfaccia) sono inutili fini a se stesse. Sono puramente degli elenchi nominali di membri astratti. |
Quando una class o una struct decide di estendere le proprie funzionalità con l'uso delle interface, lo deve fare utilizzando la separazione con il carattere virgola (,) dei tipi che vogliono usare. Sia chiaro che la classe di derivazione deve essere il primo elemento dopo il carattere dei due punti (:). |
Per sapere (in maniera dinamica) se una certa class o struct implementa una specifica interface, si può usare il cast esplicito: nel caso il type non supporti l'interfaccia richiesta, si otterrà un'eccezione di tipo InvalidCastException.
static void Main(string[] args)
{
...
// intercettare eventuali InvalidCastException
Circle c = new Circle("Lele");
IPointy itfPt = null;
try
{
itfPt = (IPointy)c;
Console.WriteLine(itfPt.Points);
}
catch (InvalidCastException iex)
{
Console.WriteLine(iex.Message);
}
Console.ReadLine();
}
|
Un altro modo per determinare se una class o struct implementa una specifica interface, è quello di usare la keywork as. In caso si ottenga un valore null, allora si avrà la certezza che la class non implementa tale interface.
static void Main(string[] args)
{
...
// intercettare eventuali InvalidCastException
Circle c = new Circle("Lele");
IPointy itfPt = c as IPointy;
if (itfPt != null)
{
Console.WriteLine(itfPt.Points);
}
else
{
Console.WriteLine("Non è di tipo IPointy");
}
Console.ReadLine();
}
|
Un altro modo per determinare se una class o struct implementa una specifica interface, è quello di usare la keywork is. Se l'oggetto non è compatibile con la specifica interface, si otterrà il valore false, altrimenti il valore sarà true e si potrà effettuare il cast in maniera sicura.
static void Main(string[] args)
{
...
// intercettare eventuali InvalidCastException
Circle c = new Circle("Lele");
if (c is IPointy)
{
Console.WriteLine(((IPointy)itfPt).Points);
}
else
{
Console.WriteLine("Non è di tipo IPointy");
}
Console.ReadLine();
}
|
In VisualStudio è possibile estrarre automaticamente un'interfaccia mediante il comando Modifica > Refactoring > Estrai interfaccia |
Dal momento che ogni class possa implementare un numero non definito di inteface, è possibile che alcune abbiano membri con gli stessi nomi, il che causerebbe impossibilità di esecuzione corretta. Per ovviare a ciò è possibile effettuare una explicit implementation mediante la formula:
returnType InterfaceName.MethodName(firma) {}
In questo modo si andrà ad assegnare alla class la corretta implementazione per ogni interface specifica. |
Le interface possono essere organizzate in gerarchie di interface; come per la gerarchia delle class, quando un'interface estende un'altra interface esistente, ne eredita i membri astratti definiti dal parent. Naturalmente, diversamente dalle class, le interface derivate non effettuano una vera implementazione: pertanto un'interface derivata semplicemente estende le definizioni con membri astratti
La gerarchia di interface può essere utile quando si vogliono estendere le funzionalità di una interface esistente senza "rompere" il codice di base. |
|