Casino online











Mercato forex






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:
Interface [Nome dell'interfaccia]
  'Membri
End Interface 
Per convenzione, il nome deve sempre iniziare con una lettera I maiuscola. La sintassi utilizzata per implementarla in una classe è questa:
Class [Nome classe]
    Implements [Nome interfaccia]
    
    [Metodo] [Parametri] Implements [Nome interfaccia].[Metodo]
End Class 
Ad esempio:
Imports Microsoft.DirectX.AudioVideoPlayback
Module Module1
    'Questa è un'interfaccia IFile: le classi che la implementeranno
    'dovranno dichiarare due proprietà readonly e tre procedure
    'senza parametri come specificato
    Interface IFile
        ReadOnly Property FileName() As String
        ReadOnly Property DirectoryName() As String
        Sub Open(ByVal FileName As 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, _DirectoryName As String
        'Oggetto che gestisce un file generico
        Private Stream As IO.FileStream
        'Come visto nella lezione sui distruttori, ci si assicura
        'che il file sia aperto con una variabile booleana
        Private IsOpen As Boolean

        Sub New(ByVal FileName As 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.DirectoryName
            Get
                Return _FileName
            End Get
        End Property

        Public ReadOnly Property FileName() As String _
            Implements IFile.FileName
            Get
                Return _DirectoryName
            End 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 IsOpen Then
                Stream.Close()
                IsOpen = False
            End If
        End Sub

        Public Sub Open(ByVal FileName As String) Implements IFile.Open
            'Se il file è aperto, lo chiude
            If IsOpen Then
                Close()
            End If
            _FileName = FileName
            _DirectoryName = IO.Path.GetDirectoryName(FileName)
            Stream = New IO.FileStream(_FileName, IO.FileMode.OpenOrCreate)
            IsOpen = True
        End Sub

        'Procedure aggiuntive tipiche solo dei file di testo
        Public Sub Write(ByVal Text As String)
            If IsOpen Then
                '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 Length As Int32) As String
            If IsOpen Then
                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 Text As String
                    Text = System.Text.ASCIIEncoding.ASCII.GetString(Bytes)
                    Return Text
                Catch Ex As 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 AudioFile
        Implements IFile

        'Variabili private necessarie alla scrittura delle proprietà
        Private _FileName, _DirectoryName As String
        'Variabile che rappresenta il file audio
        Private File As Audio
        Private IsOpen As Boolean

        Sub New(ByVal FileName As String)
            Open(FileName)
        End Sub

        Public ReadOnly Property DirectoryName() As String _
            Implements IFile.DirectoryName
            Get
                Return _DirectoryName
            End Get
        End Property

        Public ReadOnly Property FileName() As String _
            Implements IFile.FileName
            Get
                Return _FileName
            End Get
        End Property

        'Riproduce il file multimediale
        Public Sub Execute() Implements IFile.Execute
            If IsOpen Then
                File.Play()
            End If
        End Sub

        Public Sub Close() Implements IFile.Close
            If IsOpen Then
                File = Nothing
                IsOpen = False
            End If
        End Sub

        Public Sub Open(ByVal FileName As String) Implements IFile.Open
            If IsOpen Then
                Close()
            End If
            _FileName = FileName
            _DirectoryName = IO.Path.GetDirectoryName(FileName)
            File = New Audio(FileName)
            IsOpen = True
        End Sub

        'Procedura aggiuntive tipiche solo dei file musicali
        Public Sub FStop()
            If IsOpen Then
                File.Stop()
            End If
        End Sub

        Public Sub Pause()
            If IsOpen Then
                File.Pause()
            End If
        End Sub
    End Class

    Sub Main()
        Dim F As New TextFile("C:\text.txt")
        Dim A As 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) = A

        For Each I As IFile In Files
            Console.WriteLine(I.FileName)
        Next
        Console.ReadKey()
    End Sub
End Module 
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.
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.
Interface IProva1
    Structure Struttura
        Dim A, B As String
        Dim C, D As Int16
    End Structure
    Sub Procedura()
End Interface

Class Prova
    Implements IProva1
    Dim Variabile As IProva1.Struttura

    Public Sub Procedura() Implements IProva1.Procedura
        Variabile.A = "Ciao"
    End Sub
End Class 
Ma una classe può implementare anche più di una interfaccia e lo stesso membro anche più di un membro.
Interface IProva1
    Structure Struttura
        Dim A, B As String
        Dim C, D As Int16
    End Structure
    Sub Procedura()
End Interface

Interface IProva2
    Enum Enumeratore
        A
        B
        C
        D
    End Enum
    Function Funzione() As Byte
    Sub NuovaProc()
End Interface

Class Prova
    'Implementa due interfacce
    Implements IProva1, IProva2
    Dim Variabile1 As IProva1.Struttura
    Dim Variabile2 As 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.Funzione
        Return Variabile2
    End 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.