Questo sito utilizza cookies, anche di terze parti, per mostrare pubblicità e servizi in linea con il tuo account. Leggi l'informativa sui cookies.
Username: Password: oppure
Guida al Visual Basic .NET - Le Interfacce

Guida al Visual Basic .NET

Capitolo 37° - Le Interfacce

<< Precedente Prossimo >>


Scopo delle Interfacce

Le interfacce sono un'entità davvero singolare all'interno del .NET Framework. La loro funzione è assimilabile a quella delle classi astratte, ma il modo con cui esse la svolgono è molto diverso da ciò che abbiamo visto nel capitolo precedente. Il principale scopo di un'interfaccia è definire lo scheletro di una classe; potrebbe essere scherzosamente assimilata alla ricetta con cui si prepara un dolce. Quello che l'interfaccia X fa, ad esempio, consiste nel dire che per costruire una classe Y che rispetti "la ricetta" descritta in X servono una proprietà Id di tipo Integer, una funzione GetSomething senza parametri che restituisce una stringa e una procedura DoSomething con un singolo parametro Double. Tutte le classi che avranno intenzione di seguire i precetti di X (in gergo implementare X) dovranno definire, allo stesso modo, quella proprietà di quel tipo e quei metodi con quelle specifiche signature (il nome ha importanza relativa).
Faccio subito un esempio. Fino ad ora, abbiamo visto essenzialmente due tipi di collezione: gli Array e gli ArrayList. Sia per l'uno che per l'altro, ho detto che è possibile eseguire un'iterazione con il costrutto For Each:
Dim Ar() As Int32 = {1, 2, 3, 4, 5, 6}
Dim Al As New ArrayList

For I As Int32 = 1 To 40
    Al.Add(I)
Next

'Stampa i valori di Ar:
For Each K As Int32 In Ar
    Console.WriteLine(K)
Next
'Stampa i valori di Al
For Each K As Int32 In Al
    Console.WriteLine(K)
Next
Ma il sistema come fa a sapere che Ar e Al sono degli insiemi di valori? Dopotutto, il loro nome è significativo solo per noi programmatori, mentre per il calcolatore non è altro che una sequenza di caratteri. Allo stesso modo, il codice di Array e ArrayList, definito dai programmatori che hanno scritto il Framework, è intelligibile solo agli uomini, perchè al computer non comunica nulla sullo scopo per il quale è stato scritto. Allora, siamo al punto di partenza: nelle classi Array e ArrayList non c'è nulla che possa far "capire" al programma che quelli sono a tutti gli effetti delle collezioni e che, quindi, sono iterabili; e, anche se in qualche strano modo l'elaboratore lo potesse capire, non "saprebbe" (in quanto entità non senziente) come far per estrarre singoli dati e darceli uno in fila all'altro. Ecco che entrano in scena le interfacce: tutte le classi che rappresentano un insieme o una collezione di elementi implementano l'interfaccia IEnumerable, la quale, se potesse parlare, direbbe "Guarda che questa classe è una collezione, trattala di conseguenza!". Questa interfaccia obbliga le classi dalle quali è implementata a definire alcuni metodi che servono per l'enumerazione (Current, MoveNext e Reset) e che vedremo nei prossimi capitoli.
In conclusione, quindi, il For Each prima di tutto controlla che l'oggetto posto dopo la clausola "In" implementi l'interfaccia IEnumerable. Quindi richiama il metodo Reset per porsi sul primo elemento, poi deposita in K il valore esposto dalla proprietà Current, esegue il codice contenuto nel proprio corpo e, una volta arrivato a Next, esegue il metodo MoveNext per avanzare al prossimo elemento. Il For Each "è sicuro" dell'esistenza di questi membri perchè l'interfaccia IEnumerable ne impone la definizione.
Riassumendo, le interfacce hanno il compito di informare il sistema su quali siano le caratteristiche e i compiti di una classe. Per questo motivo, il loro nomi terminano spesso in "-able", come ad esempio IEnumerable, IEquatable, IComprable, che ci dicono "- è enumerabile", "- è eguagliabile", "- è comparabile", "è ... qualcosa".


Dichiarazione e implementazione

La sintassi usata per dichiarare un'interfaccia è la seguente:
Interface [Nome]
    'Membri
End Interface
I membri delle interfacce, tuttavia, sono un po' diversi dai membri di una classe, e nello scriverli bisogna rispettare queste regole:
  • Nel caso di metodi, proprietà od eventi, il corpo non va specificato;
  • Non si possono mai usare gli specificatori di accesso;
  • Si possono comunque usare dei modificatori come Shared, ReadOnly e WriteOnly.
Il primo ed il secondo punto saranno ben compresi se ci si sofferma a pensare che l'interfaccia ha il solo scopo di definire quali membri una classe debba implementare: per questo motivo, non se ne può scrivere il corpo, dato che spetta espressamente alle classi implementanti, e non ci si preoccupa dello specificatore di accesso, dato che si sta specificando solo il "cosa" e non il "come". Ecco alcuni semplici esempi di dichiarazioni:
'Questa interfaccia dal nome improbabile indica che
'la classe che la implementa rappresenta qualcosa di
'"identificabile" e per questo espone una proprietà Integer Id
'e una funzione ToString. Id e ToString, infatti, sono gli
'elementi più utili per identificare qualcosa, prima in
'base a un codice univoco e poi grazie ad una rappresentazione
'comprensibile dall'uomo
Interface IIdentifiable
    ReadOnly Property Id() As Int32
    Function ToString() As String
End Interface

'La prossima interfaccia, invece, indica qualcosa di resettabile
'e obbliga le classi implementanti a esporre il metodo Reset
'e la proprietà DefaultValue, che dovrebbe rappresentare
'il valore di default dell'oggetto. Dato che non sappiamo ora
'quali classi implementeranno questa interfaccia, dobbiamo
'per forza usare un tipo generico come Object per rappresentare
'un valore reference. Vedremo come aggirare questo ostacolo
'fra un po', con i Generics
Interface IResettable
    Property DefaultValue() As Object
    Sub Reset()
End Interface

'Come avete visto, i nomi di interfaccia iniziano per convenzione
'con la lettera I maiuscola
Ora che sappiamo come dichiarare un'interfaccia, dobbiamo scoprire come usarla. Per implementare un'interfaccia in una classe, si usa questa sintassi:
Class Example
    Implements [Nome Interfaccia]
    
    [Membro] Implements [Nome Interfaccia].[Membro]
End Class
Si capisce meglio con un esempio:
Module Module1
    Interface IIdentifiable
        ReadOnly Property Id() As Int32
        Function ToString() As String
    End Interface

    'Rappresenta un pacco da spedire
    Class Pack
        'Implementa l'interfaccia IIdentifiable, in quanto un pacco
        'dovrebbe poter essere ben identificato
        Implements IIdentifiable
        
        'Notate bene che l'interfaccia ci obbliga a definire una
        'proprietà, ma non ci obbliga a definire un campo
        'ad essa associato
        Private _Id As Int32
        Private _Destination As String
        Private _Dimensions(2) As Single

        'La classe definisce una proprietà id di tipo Integer
        'e la associa all'omonima presente nell'interfaccia in
        'questione. Il legame tra questa proprietà Id e quella
        'presenta nell'interfaccia è dato solamente dalla
        'clausola (si chiama così in gergo) "Implements",
        'la quale avvisa il sistema che il vincolo imposto
        'è stato soddisfatto.
        'N.B.: il fatto che il nome di questa proprietà sia uguale
        'a quella definita in IIdentifiable non significa nulla.
        'Avremmo potuto benissimo chiamarla "Pippo" e associarla
        'a Id tramite il codice "Implements IIdentifiable.Id", ma
        'ovviamente sarebbe stata una palese idiozia XD
        Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id
            Get
                Return _Id
            End Get
        End Property

        'Destinazione del pacco.
        'Il fatto che l'interfaccia ci obblighi a definire quei due
        'membri non significa che non possiamo definirne altri
        Public Property Destination() As String
            Get
                Return _Destination
            End Get
            Set(ByVal value As String)
                _Destination = value
            End Set
        End Property

        'Piccolo ripasso delle proprietà indicizzate e
        'della gestione degli errori
        Public Property Dimensions(ByVal Index As Int32) As Single
            Get
                If (Index >= 0) And (Index < 3) Then
                    Return _Dimensions(Index)
                Else
                    Throw New IndexOutOfRangeException()
                End If
            End Get
            Set(ByVal value As Single)
                If (Index >= 0) And (Index < 3) Then
                    _Dimensions(Index) = value
                Else
                    Throw New IndexOutOfRangeException()
                End If
            End Set
        End Property

        Public Overrides Function ToString() As String Implements IIdentifiable.ToString
            Return String.Format("{0}: Pacco {1}x{2}x{3}, Destinazione: {4}", _
                Me.Id, Me.Dimensions(0), Me.Dimensions(1), _
                Me.Dimensions(2), Me.Destination)
        End Function
    End Class
    
    Sub Main()
        '...
    End Sub
End Module
Ora che abbiamo implementato l'interfaccia nella classe Pack, tuttavia, non sappiamo che farcene. Siamo a conoscenza del fatto che gli oggetti Pack saranno sicuramente identificabili, ma nulla di più. Ritorniamo, allora, all'esempio del primo paragrafo: cos'è che rende veramente utile IEnumerable, al di là del fatto di rendere funzionante il For Each? Si applica a qualsiasi collezione o insieme, non importa di quale natura o per quali scopi, non importa nemmeno il codice che sottende all'enumerazione: l'importante è che una vastissima gamma di oggetti possano essere ricondotti ad un solo archetipo (io ne ho nominati solo due, ma ce ne sono a iosa). Allo stesso modo, potremo usare IIdentifiable per manipolare una gran quantità di dati di natura differente. Ad esempio, il codice di sopra potrebbe essere sviluppato per creare un sistema di gestione di un ufficio postale. Eccone un esempio:
Module Module1

    Interface IIdentifiable
        ReadOnly Property Id() As Int32
        Function ToString() As String
    End Interface

    Class Pack
        Implements IIdentifiable

        Private _Id As Int32
        Private _Destination As String
        Private _Dimensions(2) As Single

        Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id
            Get
                Return _Id
            End Get
        End Property

        Public Property Destination() As String
            Get
                Return _Destination
            End Get
            Set(ByVal value As String)
                _Destination = value
            End Set
        End Property

        Public Property Dimensions(ByVal Index As Int32) As Single
            Get
                If (Index >= 0) And (Index < 3) Then
                    Return _Dimensions(Index)
                Else
                    Throw New IndexOutOfRangeException()
                End If
            End Get
            Set(ByVal value As Single)
                If (Index >= 0) And (Index < 3) Then
                    _Dimensions(Index) = value
                Else
                    Throw New IndexOutOfRangeException()
                End If
            End Set
        End Property

        Sub New(ByVal Id As Int32)
            _Id = Id
        End Sub

        Public Overrides Function ToString() As String Implements IIdentifiable.ToString
            Return String.Format("{0:0000}: Pacco {1}x{2}x{3}, Destinazione: {4}", _
                Me.Id, Me.Dimensions(0), Me.Dimensions(1), _
                Me.Dimensions(2), Me.Destination)
        End Function
    End Class

    Class Telegram
        Implements IIdentifiable

        Private _Id As Int32
        Private _Recipient As String
        Private _Message As String

        Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id
            Get
                Return _Id
            End Get
        End Property

        Public Property Recipient() As String
            Get
                Return _Recipient
            End Get
            Set(ByVal value As String)
                _Recipient = value
            End Set
        End Property

        Public Property Message() As String
            Get
                Return _Message
            End Get
            Set(ByVal value As String)
                _Message = value
            End Set
        End Property

        Sub New(ByVal Id As Int32)
            _Id = Id
        End Sub

        Public Overrides Function ToString() As String Implements IIdentifiable.ToString
            Return String.Format("{0:0000}: Telegramma per {1} ; Messaggio = {2}", _
                Me.Id, Me.Recipient, Me.Message)
        End Function
    End Class

    Class MoneyOrder
        Implements IIdentifiable

        Private _Id As Int32
        Private _Recipient As String
        Private _Money As Single

        Public ReadOnly Property Id() As Integer Implements IIdentifiable.Id
            Get
                Return _Id
            End Get
        End Property

        Public Property Recipient() As String
            Get
                Return _Recipient
            End Get
            Set(ByVal value As String)
                _Recipient = value
            End Set
        End Property

        Public Property Money() As Single
            Get
                Return _Money
            End Get
            Set(ByVal value As Single)
                _Money = value
            End Set
        End Property

        Sub New(ByVal Id As Int32)
            _Id = Id
        End Sub

        Public Overrides Function ToString() As String Implements IIdentifiable.ToString
            Return String.Format("{0:0000}: Vaglia postale per {1} ; Ammontare = {2}?", _
                Me.Id, Me.Recipient, Me.Money)
        End Function
    End Class

    'Classe che elabora dati di tipo IIdentifiable, ossia qualsiasi
    'oggetto che implementi tale interfaccia
    Class PostalProcessor
        'Tanto per tenersi allenati coi delegate, ecco una
        'funzione delegate che funge da filtro per i vari id
        Public Delegate Function IdSelector(ByVal Id As Int32) As Boolean

        Private _StorageCapacity As Int32
        Private _NextId As Int32 = 0
        'Un array di interfacce. Quando una variabile viene
        'dichiarata come di tipo interfaccia, ciò
        'che può contenere è qualsiasi oggetto
        'che implementi quell'interfaccia. Per lo stesso
        'discorso fatto nel capitolo precedente, noi
        'possiamo vedere attraverso l'interfaccia
        'solo quei membri che essa espone direttamente, anche
        'se il contenuto vero e proprio è qualcosa
        'di più
        Private Storage() As IIdentifiable

        'Capacità del magazzino. Assumeremo che tutti
        'gli oggetti rappresentati dalle classi Pack, Telegram
        'e MoneyOrder vadano in un magazzino immaginario che,
        'improbabilmente, riserva un solo posto per ogni
        'singolo elemento
        Public Property StorageCapacity() As Int32
            Get
                Return _StorageCapacity
            End Get
            Set(ByVal value As Int32)
                _StorageCapacity = value
                ReDim Preserve Storage(value)
            End Set
        End Property

        'Modifica od ottiene un riferimento all'Index-esimo
        'oggetto nell'array Storage
        Public Property Item(ByVal Index As Int32) As IIdentifiable
            Get
                If (Index >= 0) And (Index < Storage.Length) Then
                    Return Me.Storage(Index)
                Else
                    Throw New IndexOutOfRangeException()
                End If
            End Get
            Set(ByVal value As IIdentifiable)
                If (Index >= 0) And (Index < Storage.Length) Then
                    Me.Storage(Index) = value
                Else
                    Throw New IndexOutOfRangeException()
                End If
            End Set
        End Property

        'Restituisce la prima posizione libera nell'array
        'Storage. Anche se in questo esempio non l'abbiamo
        'contemplato, gli elementi possono anche essere rimossi
        'e quindi lasciare un posto libero nell'array
        Public ReadOnly Property FirstPlaceAvailable() As Int32
            Get
                For I As Int32 = 0 To Me.Storage.Length - 1
                    If Me.Storage(I) Is Nothing Then
                        Return I
                    End If
                Next
                Return (-1)
            End Get
        End Property

        'Tutti gli oggetti che inizializzeremo avranno bisogno
        'di un id: ce lo fornisce la stessa classe Processor
        'tramite questa proprietà che si autoincrementa
        Public ReadOnly Property NextId() As Int32
            Get
                _NextId += 1
                Return _NextId
            End Get
        End Property


        'Due possibili costruttori: uno che accetta un insieme
        'già formato di elementi...
        Public Sub New(ByVal Items() As IIdentifiable)
            Me.Storage = Items
            _SorageCapacity = Items.Length
        End Sub

        '... e uno che accetta solo la capacità del magazzino
        Public Sub New(ByVal Capacity As Int32)
            Me.StorageCapacity = Capacity
        End Sub

        'Stampa a schermo tutti gli elementi che la funzione
        'contenuta nel parametro Selector di tipo delegate
        'considera validi (ossia tutti quelli per cui
        'Selector.Invoke restituisce True)
        Public Sub PrintByFilter(ByVal Selector As IdSelector)
            For Each K As IIdentifiable In Storage
                If K Is Nothing Then
                    Continue For
                End If
                If Selector.Invoke(K.Id) Then
                    Console.WriteLine(K.ToString())
                End If
            Next
        End Sub

        'Stampa l'oggetto con Id specificato
        Public Sub PrintById(ByVal Id As Int32)
            For Each K As IIdentifiable In Storage
                If K Is Nothing Then
                    Continue For
                End If
                If K.Id = Id Then
                    Console.WriteLine(K.ToString())
                    Exit For
                End If
            Next
        End Sub

        'Cerca tutti gli elementi che contemplano all'interno
        'della propria descrizione la stringa Str e li
        'restituisce come array di Id
        Public Function SearchItems(ByVal Str As String) As Int32()
            Dim Temp As New ArrayList

            For Each K As IIdentifiable In Storage
                If K Is Nothing Then
                    Continue For
                End If
                If K.ToString().Contains(Str) Then
                    Temp.Add(K.Id)
                End If
            Next

            Dim Result(Temp.Count - 1) As Int32
            For I As Int32 = 0 To Temp.Count - 1
                Result(I) = Temp(I)
            Next

            Temp.Clear()
            Temp = Nothing

            Return Result
        End Function
    End Class

    Private Processor As New PostalProcessor(10)
    Private Cmd As Char
    Private IdFrom, IdTo As Int32

    Function SelectId(ByVal Id As Int32) As Boolean
        Return (Id >= IdFrom) And (Id <= IdTo)
    End Function

    Sub InsertItems(ByVal Place As Int32)
        Console.WriteLine("Scegliere la tipologia di oggetto:")
        Console.WriteLine(" p - pacco;")
        Console.WriteLine(" t - telegramma;")
        Console.WriteLine(" v - vaglia postale;")
        Cmd = Console.ReadKey().KeyChar

        Console.Clear()
        Select Case Cmd
            Case "p"
                Dim P As New Pack(Processor.NextId)
                Console.WriteLine("Pacco - Id:{0:0000}", P.Id)
                Console.Write("Destinazione: ")
                P.Destination = Console.ReadLine
                Console.Write("Larghezza: ")
                P.Dimensions(0) = Console.ReadLine
                Console.Write("Lunghezza: ")
                P.Dimensions(1) = Console.ReadLine
                Console.Write("Altezza: ")
                P.Dimensions(2) = Console.ReadLine
                Processor.Item(Place) = P
            Case "t"
                Dim T As New Telegram(Processor.NextId)
                Console.WriteLine("Telegramma - Id:{0:0000}", T.Id)
                Console.Write("Destinatario: ")
                T.Recipient = Console.ReadLine
                Console.Write("Messaggio: ")
                T.Message = Console.ReadLine
                Processor.Item(Place) = T
            Case "v"
                Dim M As New MoneyOrder(Processor.NextId)
                Console.WriteLine("Vaglia - Id:{0:0000}", M.Id)
                Console.Write("Beneficiario: ")
                M.Recipient = Console.ReadLine
                Console.Write("Somma: ")
                M.Money = Console.ReadLine
                Processor.Item(Place) = M
            Case Else
                Console.WriteLine("Comando non riconosciuto.")
                Console.ReadKey()
                Exit Sub
        End Select

        Console.WriteLine("Inserimento eseguito!")
        Console.ReadKey()
    End Sub

    Sub ProcessData()
        Console.WriteLine("Selezionare l'operazione:")
        Console.WriteLine(" c - cerca;")
        Console.WriteLine(" v - visualizza;")
        Cmd = Console.ReadKey().KeyChar

        Console.Clear()
        Select Case Cmd
            Case "c"
                Dim Str As String
                Console.WriteLine("Inserire la parola da cercare:")
                Str = Console.ReadLine

                Dim Ids() As Int32 = Processor.SearchItems(Str)
                Console.WriteLine("Trovati {0} elementi. Visualizzare? (y/n)", Ids.Length)
                Cmd = Console.ReadKey().KeyChar
                Console.WriteLine()

                If Cmd = "y" Then
                    For Each Id As Int32 In Ids
                        Processor.PrintById(Id)
                    Next
                End If
            Case "v"
                Console.WriteLine("Visualizzare gli elementi")
                Console.Write("Da Id: ")
                IdFrom = Console.ReadLine
                Console.Write("A Id: ")
                IdTo = Console.ReadLine
                Processor.PrintByFilter(AddressOf SelectId)
            Case Else
                Console.WriteLine("Comando sconosciuto.")
        End Select

        Console.ReadKey()
    End Sub

    Sub Main()
        Do
            Console.WriteLine("Gestione ufficio")
            Console.WriteLine()
            Console.WriteLine("Selezionare l'operazione da effettuare:")
            Console.WriteLine(" i - inserimento oggetti;")
            Console.WriteLine(" m - modifica capacità magazzino;")
            Console.WriteLine(" p - processa i dati;")
            Console.WriteLine(" e - esci.")
            Cmd = Console.ReadKey().KeyChar

            Console.Clear()
            Select Case Cmd
                Case "i"
                    Dim Index As Int32 = Processor.FirstPlaceAvailable

                    Console.WriteLine("Inserimento oggetti in magazzino")
                    Console.WriteLine()

                    If Index > -1 Then
                        InsertItems(Index)
                    Else
                        Console.WriteLine("Non c'è più spazio in magazzino!")
                        Console.ReadKey()
                    End If
                Case "m"
                    Console.WriteLine("Attuale capacità: " & Processor.StorageCapacity)
                    Console.WriteLine("Inserire una nuova dimensione: ")
                    Processor.StorageCapacity = Console.ReadLine
                    Console.WriteLine("Operazione effettuata.")
                    Console.ReadKey()
                Case "p"
                    ProcessData()
            End Select
            Console.Clear()
        Loop Until Cmd = "e"
    End Sub
End Module
Avevo in mente di definire anche un'altra interfaccia, IPayable, per calcolare anche il costo di spedizione di ogni pezzo: volevo far notare come, sebbene il costo vada calcolato in maniera diversa per i tre tipi di oggetto (in base alle dimensioni per il pacco, in base al numero di parole per il telegramma e in base all'ammontare inviato per il vaglia), bastasse richiamare una funzione attraverso l'interfaccia per ottenere il risultato. Poi ho considerato che un esempio di 400 righe era già abbastanza. Ad ogni modo, userò adesso quel'idea in uno spezzone tratto dal programma appena scritto per mostrare l'uso di interfacce multiple:
Module Module1
    '...
    
    Interface IPayable
        Function CalculateSendCost() As Single
    End Interface
    
    Class Telegram
        'Nel caso di più interfacce, le si separa con la virgola
        Implements IIdentifiable, IPayable

        '...

        Public Function CalculateSendCost() As Single Implements IPayable.CalculateSendCost
            'Come vedremo nel capitolo dedicato alle stringhe,
            'la funzione Split(c) spezza la stringa in tante
            'parti, divise dal carattere c, e le restituisce
            'sottoforma di array. In questo caso, tutte le sottostringhe
            'separate da uno spazio sono all'incirca tante
            'quanto il numero di parole nella frase
            Select Case Me.Message.Split(" ").Length
                Case Is <= 20
                    Return 4.39
                Case Is <= 50
                    Return 6.7
                Case Is <= 100
                    Return 10.3
                Case Is <= 200
                    Return 19.6
                Case Is <= 500
                    Return 39.75
            End Select
        End Function
    End Class
    
    '...
End Class


Definizione di tipi in un'interfaccia

Così come è possibile dichiarare una nuova classe all'interno di un'altra, o una struttura in una classe, o un'interfaccia in una classe, o una struttura in una struttura, o tutte le altre possibili combinazioni, è anche possibile dichiarare un nuovo tipo in un'interfaccia. In questo caso, solo le classi che implementeranno quell'interfaccia saranno in grado di usare quel tipo. Ad esempio:
Interface ISaveable
    Structure FileInfo
        'Assumiamo per brevità che queste variabili Public
        'siano in realtà proprietà
        Public Path As String
        'FileAttribues è un enumeratore su bit che contiene
        'informazioni sugli attributi di un file (nascosto, a sola
        'lettura, archivio, compresso, eccetera...)
        Public Attributes As FileAttributes
    End Structure
    Property SaveInfo() As FileInfo
    Sub Save()
End Interface

Class A
    Private _SaveInfo As ISaveable.FileInfo 'SBAGLIATO!
    '...
End Class


Class B
    Implements ISaveable
    
    Private _SaveInfo As ISaveable.FileInfo 'GIUSTO
    
    '...
End Class


Ereditarietà, polimorfismo e overloading per le interfacce

Anche le interfacce possono ereditare da un'altra interfaccia base. In questo caso, dato che in un'interfaccia non si possono usare specificatori di accesso, la classe derivata acquisisce tutti i membri di quella base:
Interface A
    Property PropA() As Int32
End Interface

Interface B
    Inherits A
    Sub SubB()
End Interface
Non si può usare il polimorfismo perchè non c'è nulla da ridefinire, in quanto i metodi non hanno un corpo.
Si può, invece, usare l'overloading come si fa di consueto: non ci sono differenze significative in questo ambito.


Perchè preferire un'interfaccia a una classe astratta

Ci sono casi e casi. In genere, un'interfaccia va preferita quando si dovranno abbracciare grandi quantità di classi diverse: con grandi mi riferisco a numeri abbastanza grandi da rendere difficile l'uso di una sola classe astratta. È bene usare le interfacce anche quando si progetta di usarne più di una: una classe, infatti, può implementare quante interfacce vuole, ma può ereditare da una sola classe base.
Proprio per questi motivi, è ideale usare un'interfaccia quando si vuole delineare una caratteristica o un particolare comportamento che può essere comune a più tipi, mentre è meglio puntare a una classe astratta quando si vuole sottolineare l'appartenenza di molti tipi ad un unico archetipo base.


<< Precedente Prossimo >>
A proposito dell'autore

Programmatore e analista .NET 2005/2008/2010 (in particolare C# e VB.NET), anche nell'implementazione Mono per Linux. Conoscenze approfondite di Pascal, PHP, XML, HTML 4.01/5, CSS 2.1/3, Javascript (e jQuery). Conoscenze buone di C, LUA, GML, Ruby, XNA, AJAX e Assembly 68000. Competenze basilari di C++, SQL, Hlsl, Java.