A36. Interfacce
Dichiarazione e implementazione
Le interfacce potrebbero sembrare simili alle classi astratte, ma non lo sono. Definiscono la struttura che una classe deve avere, ma non
possono in nessun modo implementare del codice eseguibile: non possono, cioè, essere dichiarti i copri di metodi o proprietà
come avviene nelle classi. Si definiscono solamente metodi, proprietà, eventi o tipi (strutture, enumeratori, delegate)
specificandone la signature ed il nome, che dovranno mantenersi costanti nelle classi che implementeranno quell'interfaccia.
I vantaggi di usare un'interfaccia al posto di una classe sono sostanzialmente due: rendono il codice definito per l'interfaccia assai più riusabile
di quello definito per la classe; una classe non può ereditare più di una base, ma può implementare quante inerfacce vuole. La sintassi
di un'interfaccia è questa:
Per convenzione, il nome deve sempre iniziare con una lettera I maiuscola. La sintassi utilizzata per implementarla in una classe è questa:Interface [Nome dell'interfaccia] 'Membri End Interface
Ad esempio:Class [Nome classe]Implements [Nome interfaccia] [Metodo] [Parametri]Implements [Nome interfaccia].[Metodo]End Class
Nell'esempio si osserva come vengano definiti i membri di un'interfaccia nella classe che la implementa. Bisogna anche notare che gli specificatori di accesso sono stati posti solamente nell'effettiva versione implementata del membro e non nella definizione dell'interfaccia. Utilizzando questo codice si può fornire molta flessibilità ad un'applicazione: ad esempio, se volessimo scrivere un programma che permette di eseguire certi file ad un certo orario, potremmo usare una collezione di IFile e richiamare il metodo Execute, che appartiene a tutti gli oggetti implementati mediante questa interfaccia. Così, dopo aver completato di scrivere anche classi apposite per video, programmi, immagini e quant'altro, sarebbe possibile con pochissimo codice eseguirli tutti senza fare distinzione. Questa è una delle maggiori potenzialità di questo costrutto.Imports Microsoft.DirectX.AudioVideoPlaybackModule Module1 'Questa è un'interfaccia IFile: le classi che la implementeranno 'dovranno dichiarare due proprietà readonly e tre procedure 'senza parametri come specificato Interface IFileReadOnly Property FileName()As String ReadOnly Property DirectoryName()As String Sub Open(ByVal FileNameAs String )Sub Close()Sub Execute()End Interface Class TextFile 'Implementa l'interfaccia IFile: questa istruzione è 'un pò come ereditare una classe, poichè un oggetto TextFile 'potrà benissimo essere convertito in un generico oggetto 'IFile usando l'operatore DirectCast Implements IFile 'Variabili private necessarie alla scrittura delle proprietà Private _FileName, _DirectoryNameAs String 'Oggetto che gestisce un file generico Private StreamAs IO.FileStream 'Come visto nella lezione sui distruttori, ci si assicura 'che il file sia aperto con una variabile booleana Private IsOpenAs Boolean Sub New (ByVal FileNameAs String ) Open(FileName)End Sub 'L'istruzione Implements indica al compilatore qual è 'il membro che sta venendo ridefinito Public ReadOnly Property DirectoryName()As String _Implements IFile.DirectoryNameGet Return _FileNameEnd Get End Property Public ReadOnly Property FileName()As String _Implements IFile.FileNameGet Return _DirectoryNameEnd Get End Property Public Sub Execute()Implements IFile.Execute 'Apre il file di testo con il blocco note 'La funzione Shell scrive un comando sulla console, 'come se venisse scritto sul prompt dei comandi: si 'specifica il percorso del notepad e il percorso del file 'da aprire racchiuso da virgolette (chr(34)) Shell("C:\WINDOWS\System32\notepad.exe " & Chr(34) _ & _FileName & Chr(34))End Sub Public Sub Close()Implements IFile.Close 'Solo se il file è aperto lo si può chiudere If IsOpenThen Stream.Close() IsOpen = FalseEnd If End Sub Public Sub Open(ByVal FileNameAs String )Implements IFile.Open 'Se il file è aperto, lo chiude If IsOpenThen Close()End If _FileName = FileName _DirectoryName = IO.Path.GetDirectoryName(FileName) Stream =New IO.FileStream(_FileName, IO.FileMode.OpenOrCreate) IsOpen = TrueEnd Sub 'Procedure aggiuntive tipiche solo dei file di testo Public Sub Write(ByVal TextAs String )If IsOpenThen 'Converte il testo in bytes, poichè la classe Stream 'opera a basso livello e richiede solo sequenze di 'bytes come input Dim B()As Byte = System.Text.ASCIIEncoding.ASCII.GetBytes(Text) 'Scrive B.Length bytes a partire dall'indice 0, prelevando 'l'input dall'array Bytes Stream.Write(B, 0, B.Length)End If End Sub Public Function Read(ByVal LengthAs Int32)As String If IsOpenThen Dim Bytes(Length - 1)As Byte 'Legge Length bytes dal file, a partire dalla posizione a 'cui si è arrivati nella lettura, e li deposita in Bytes Try Stream.Read(Bytes, Stream.Position, Length) 'Dichiarare la variabile Text qui ha la funzione di 'risparmiare memoria, poichè se si verifica un errore 'nell'istruzione precedente, questa viene saltata Dim TextAs String Text = System.Text.ASCIIEncoding.ASCII.GetString(Bytes)Return TextCatch ExAs Exception 'Potrebbe verificarsi un errore quando si tenta di 'leggere un numero di bytes maggiore alla capienza del 'file, quindi evitiamo di mandare in crash il programma Close() Console.WriteLine(Ex.Message)End Try End If End Function End Class 'Questa classe utilizza il namespace 'Microsoft.DirectX.AudioVideoPlayback: per maggiori informazioni 'sul suo utilizzo vedere capitolo D3 Class AudioFileImplements IFile 'Variabili private necessarie alla scrittura delle proprietà Private _FileName, _DirectoryNameAs String 'Variabile che rappresenta il file audio Private FileAs AudioPrivate IsOpenAs Boolean Sub New (ByVal FileNameAs String ) Open(FileName)End Sub Public ReadOnly Property DirectoryName()As String _Implements IFile.DirectoryNameGet Return _DirectoryNameEnd Get End Property Public ReadOnly Property FileName()As String _Implements IFile.FileNameGet Return _FileNameEnd Get End Property 'Riproduce il file multimediale Public Sub Execute()Implements IFile.ExecuteIf IsOpenThen File.Play()End If End Sub Public Sub Close()Implements IFile.CloseIf IsOpenThen File = Nothing IsOpen = FalseEnd If End Sub Public Sub Open(ByVal FileNameAs String )Implements IFile.OpenIf IsOpenThen Close()End If _FileName = FileName _DirectoryName = IO.Path.GetDirectoryName(FileName) File =New Audio(FileName) IsOpen = TrueEnd Sub 'Procedura aggiuntive tipiche solo dei file musicali Public Sub FStop()If IsOpenThen File.Stop()End If End Sub Public Sub Pause()If IsOpenThen File.Pause()End If End Sub End Class Sub Main()Dim FAs New TextFile("C:\text.txt")Dim AAs New AudioFile("C:\music.mp3") 'A ed F godono delle stesse proprietà e metodi definiti 'da IFile Console.WriteLine(F.FileName) Console.WriteLine(A.FileName) F.Close() A.Close() 'Implementando la stessa interfaccia, si possono definire 'array o collezioni di file testuali e musicali Dim Files(1)As IFile Files(0) = F Files(1) = AFor Each IAs IFileIn Files Console.WriteLine(I.FileName)Next Console.ReadKey()End Sub End Module
Nell'esempio precedente, non si è visto come poter dichiarare anche tipi nel corpo di un'interfaccia: tale operazione viene portata a termine nel modo consueto, usando la stessa sintassi, e ogni tipo definito può essere utilizzato solamente all'interno degli oggetti che la usano.
Ma una classe può implementare anche più di una interfaccia e lo stesso membro anche più di un membro.Interface IProva1Structure StrutturaDim A, BAs String Dim C, DAs Int16End Structure Sub Procedura()End Interface Class ProvaImplements IProva1Dim VariabileAs IProva1.StrutturaPublic Sub Procedura()Implements IProva1.Procedura Variabile.A = "Ciao"End Sub End Class
Interface IProva1Structure StrutturaDim A, BAs String Dim C, DAs Int16End Structure Sub Procedura()End Interface Interface IProva2Enum Enumeratore A B C DEnd Enum Function Funzione()As Byte Sub NuovaProc()End Interface Class Prova 'Implementa due interfacce Implements IProva1, IProva2Dim Variabile1As IProva1.StrutturaDim Variabile2As IProva2.Enumeratore 'La stessa procedura implementa due procedura differenti da 'due interfacce differenti: il nome può anche essere diverso Public Sub Procedura()Implements IProva1.Procedura, IProva2.NuovaProc Console.WriteLine("Ciao")End Sub Function Funzione()As Byte Implements IProva2.FunzioneReturn Variabile2End Function End Class
Ereditarietà e Polimorfismo delle interfacce
Eh sì, purtroppo o per fortuna, anche le interfacce godono di queste caratteristiche. La prima permette di ereditare una e una sola interfaccia
base in una derivata. Le regole dell'ereditaerietà sono le stesse, tranne che per le keyword di scope che, come si è visto, non possono
essere usate in questo ambito. Se esistono membri con lo stesso nome del contesto della derivata, questi oscurano i membri base omonimi, ma
il compilatore non manca di lanciare un warning (ossia un avvertimento) su ciò che sta accadendo: si può evitare la segnalazione usando
la keyword Shadows, già descritta nel capitolo 30. In questi casi, tuttavia, la classe li deve implementare entrambi anche se hanno lo stesso
nome. Come risulterà naturale, poi, non è possibile utilizzare polimorfismo all'interno delle interfacce, poichè non si può
ridefinire il corpo dei membri (dato che non esiste). Il membro ridefinito nella classe, tuttavia, può benissimo subire polimorfismo come
sempre.
The Totem's Lair - Copyright (C) 2009
È vietata la riproduzione sia totale che parziale del sito.



