Interfacce esplicite
Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 28 di 70.
Quando una classe implementa più interfacce che hanno membri con lo stesso nome, si verifica una collisione di nomi. L'implementazione esplicita delle interfacce risolve il problema consentendoti di fornire implementazioni separate per ciascuna interfaccia.
Con un'implementazione esplicita, anteponi il nome dell'interfaccia al nome del membro e ometti il modificatore di accesso:
public interface IAmericanPlug
{
void Connect();
}
public interface IEuropeanPlug
{
void Connect();
}
public class UniversalAdapter : IAmericanPlug, IEuropeanPlug
{
void IAmericanPlug.Connect()
{
Console.WriteLine("Connected to 120V");
}
void IEuropeanPlug.Connect()
{
Console.WriteLine("Connected to 220V");
}
}I membri implementati esplicitamente sono accessibili solo tramite una variabile del tipo dell’interfaccia, non tramite la classe stessa:
var adapter = new UniversalAdapter();
// adapter.Connect(); // Non compila!
IAmericanPlug american = adapter;
american.Connect(); // Collegato a 120V
IEuropeanPlug european = adapter;
european.Connect(); // Collegato a 220VQuesta tecnica è utile anche quando vuoi nascondere i membri dell'interfaccia dall'API pubblica della classe. Se un metodo ha senso solo quando si considera l'oggetto come un'interfaccia specifica, l'implementazione esplicita mantiene pulita la superficie della tua classe, pur rispettando il contratto.
Sfida
FacileCostruiamo un sistema di generazione di report che mostri come l'implementazione esplicita di un'interfaccia consenta a una singola classe di fornire comportamenti diversi a seconda dell'interfaccia utilizzata. Creerai un documento che può essere formattato in modo diverso per la visualizzazione e per la stampa.
Organizzerai il codice in quattro file:
IDisplayable.cs: Definisci un'interfaccia chiamataIDisplayablenello spazio dei nomiReports. Questa interfaccia rappresenta contenuti che possono essere visualizzati sullo schermo. Deve dichiarare un metodoRender()che restituisce una stringa.IPrintable.cs: Definisci un'interfaccia chiamataIPrintablenello spazio dei nomiReports. Questa interfaccia rappresenta contenuti che possono essere inviati a una stampante. Deve dichiarare anche un metodoRender()che restituisce una stringa: nota che questo crea una collisione di nomi conIDisplayable!Report.cs: Definisci una classeReportnello spazio dei nomiReportsche implementa siaIDisplayablesiaIPrintable. Il report deve avere una proprietàTitle(string) e una proprietàContent(string), con un costruttore che imposta entrambi i valori. Usa l'implementazione esplicita dell'interfaccia per fornire comportamenti diversi perRender():- Quando viene visualizzato:
"[SCREEN] {Title}: {Content}" - Quando viene stampato:
"[PRINT] {Title} | {Content}"
- Quando viene visualizzato:
Program.cs: Nel file principale, crea unReportusando i valori di input. Poiché è possibile accedere alle implementazioni esplicite solo tramite variabili di interfaccia, assegna lo stesso report sia a una variabileIDisplayablesia a una variabileIPrintable, poi chiamaRender()su ciascuna per mostrare come lo stesso oggetto produca output diversi a seconda dell'interfaccia usata.
Riceverai due input:
- Titolo del report
- Contenuto del report
Stampa l'output in questo formato:
Display version:
{Render() via IDisplayable}
Print version:
{Render() via IPrintable}Per esempio, se gli input sono Sales Report e Q4 revenue increased by 15%, l'output dovrebbe essere:
Display version:
[SCREEN] Sales Report: Q4 revenue increased by 15%
Print version:
[PRINT] Sales Report | Q4 revenue increased by 15%Nota come lo stesso oggetto Report si comporti diversamente a seconda del riferimento all'interfaccia usato per accedervi. Questo è il potere dell'implementazione esplicita delle interfacce: risolvere le collisioni di nomi fornendo al contempo un comportamento appropriato al contesto!
Provalo tu
using System;
using Reports;
class Program
{
public static void Main(string[] args)
{
// Leggi l'input
string title = Console.ReadLine();
string content = Console.ReadLine();
// TODO: Crea un oggetto Report con il titolo e il contenuto
// TODO: Memorizza il report in una variabile IDisplayable
// TODO: Memorizza lo stesso report in una variabile IPrintable
// TODO: Stampa la versione per la visualizzazione chiamando Render() su IDisplayable
Console.WriteLine("Display version:");
// Stampa qui il risultato del rendering
// TODO: Stampa la versione per la stampa chiamando Render() su IPrintable
Console.WriteLine("Print version:");
// Stampa qui il risultato del rendering
}
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniNamespace e direttiveIntroduzione a classi e oggettiLa parola chiave "this"Metodi e parametriCampi e proprietàCostruttoriInizializzatori di oggettiRiepilogo - Calcolatrice semplice4Ereditarietà
Sintassi di base dell'ereditarietà (:)La parola chiave 'base'Parole chiave virtual e overrideClassi sealedLa classe base 'object'Riepilogo - Gerarchia dei dipendenti7Funzionalità avanzate
Sovraccarico degli operatoriIndicizzatori (this[])Override di ToString()Metodi di estensioneRiepilogo - Lista personalizzata10Pattern di progettazione Parte 1
Introduzione ai pattern di progettazioneSingleton thread-safePattern FactoryPattern Observer (eventi)Pattern Strategy2Proprietà e membri statici
Proprietà implementate automaticamenteProprietà di sola lettura e di sola scritturaCampi e metodi staticiClassi staticheMembri con corpo di espressione5Polimorfismo e interfacce
Polimorfismo a compile time e runtimeInterfaccia o classe astrattaInterfacce multipleInterfacce espliciteUpcasting e downcastingRipasso: calcolatore di forme8Concetti avanzati di programmazione a oggetti
Composizione invece dell'ereditarietàTipi generici (classi e metodi)Delegati ed eventiAttributi e reflectionIDisposable e istruzione usingNozioni di base sull'iniezione delle dipendenze11Pattern di progettazione, parte 2
Pattern del ComandoPattern dell’AdattatorePattern del DecoratorePattern Template MethodPattern dello StatoPattern Composito3Architettura delle classi
Dati di istanza vs staticiParole chiave 'readonly' e 'const'Campi di supportoRiepilogo - Gestore di conti bancari6Incapsulamento
Modificatori di accessoProprietà per l'incapsulamentoImplementazione dell'occultamento dei datiModelli di immutabilitàRiepilogo - Registri degli studentiEsercitati da solo: Compilatore C# online