Menu
Coddy logo textTech

Explizite Interfaces

Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy — Lektion 28 von 70.

Wenn eine Klasse mehrere Schnittstellen implementiert, die Member mit demselben Namen haben, tritt ein Namenskonflikt auf. Die explizite Schnittstellenimplementierung löst dies, indem sie es Ihnen ermöglicht, separate Implementierungen für jede Schnittstelle bereitzustellen.

Bei der expliziten Implementierung stellen Sie dem Membernamen den Schnittstellennamen voran und lassen den Zugriffsmodifizierer weg:

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");
    }
}

Explizit implementierte Member können nur über eine Variable des Schnittstellentyps aufgerufen werden, nicht über die Klasse selbst:

var adapter = new UniversalAdapter();
// adapter.Connect();  // Kompiliert nicht!

IAmericanPlug american = adapter;
american.Connect();  // Verbunden mit 120V

IEuropeanPlug european = adapter;
european.Connect();  // Verbunden mit 220V

Diese Technik ist auch nützlich, wenn Sie Interface-Member vor der öffentlichen API der Klasse verbergen möchten. Wenn eine Methode nur dann sinnvoll ist, wenn das Objekt als ein bestimmtes Interface behandelt wird, hält die explizite Implementierung die Oberfläche Ihrer Klasse sauber, während der Vertrag dennoch erfüllt wird.

challenge icon

Aufgabe

Einfach

Lassen Sie uns ein Bericht-Generator-System erstellen, das demonstriert, wie die explizite Schnittstellenimplementierung es einer einzelnen Klasse ermöglicht, unterschiedliche Verhaltensweisen bereitzustellen, je nachdem, welche Schnittstelle verwendet wird. Sie werden ein Dokument erstellen, das für die Anzeige auf dem Bildschirm anders formatiert werden kann als für den Druck.

Sie werden Ihren Code in vier Dateien organisieren:

  • IDisplayable.cs: Definieren Sie eine Schnittstelle namens IDisplayable im Namespace Reports. Diese Schnittstelle repräsentiert Inhalte, die auf dem Bildschirm angezeigt werden können. Sie sollte eine Methode Render() deklarieren, die einen String zurückgibt.
  • IPrintable.cs: Definieren Sie eine Schnittstelle namens IPrintable im Namespace Reports. Diese Schnittstelle repräsentiert Inhalte, die an einen Drucker gesendet werden können. Sie sollte ebenfalls eine Methode Render() deklarieren, die einen String zurückgibt – beachten Sie, dass dies eine Namenskollision mit IDisplayable verursacht!
  • Report.cs: Definieren Sie eine Klasse Report im Namespace Reports, die sowohl IDisplayable als auch IPrintable implementiert. Der Bericht sollte eine Eigenschaft Title (String) und eine Eigenschaft Content (String) haben, mit einem Konstruktor, der beide Werte setzt. Verwenden Sie die explizite Schnittstellenimplementierung, um unterschiedliche Render()-Verhaltensweisen bereitzustellen:
    • Bei der Darstellung für die Anzeige: "[SCREEN] {Title}: {Content}"
    • Bei der Darstellung für den Druck: "[PRINT] {Title} | {Content}"
  • Program.cs: Erstellen Sie in Ihrer Hauptdatei ein Report-Objekt unter Verwendung von Eingabewerten. Da auf explizite Implementierungen nur über Schnittstellenvariablen zugegriffen werden kann, speichern Sie denselben Bericht sowohl in einer IDisplayable-Variable als auch in einer IPrintable-Variable. Rufen Sie dann Render() für beide auf, um zu zeigen, wie dasselbe Objekt je nach verwendeter Schnittstelle unterschiedliche Ausgaben erzeugt.

Sie erhalten zwei Eingaben:

  • Berichtstitel
  • Berichtsinhalt

Geben Sie die Ausgabe in diesem Format aus:

Display version:
{Render() via IDisplayable}
Print version:
{Render() via IPrintable}

Wenn die Eingaben beispielsweise Sales Report und Q4 revenue increased by 15% sind, sollte die Ausgabe wie folgt aussehen:

Display version:
[SCREEN] Sales Report: Q4 revenue increased by 15%
Print version:
[PRINT] Sales Report | Q4 revenue increased by 15%

Beachten Sie, wie sich dasselbe Report-Objekt unterschiedlich verhält, je nachdem, über welche Schnittstellenreferenz Sie darauf zugreifen. Dies ist die Stärke der expliziten Schnittstellenimplementierung – das Lösen von Namenskollisionen bei gleichzeitiger Bereitstellung von kontextgerechtem Verhalten!

Spickzettel

Wenn eine Klasse mehrere Schnittstellen implementiert, deren Member denselben Namen haben, können Sie die explizite Schnittstellenimplementierung verwenden, um separate Implementierungen für jede Schnittstelle bereitzustellen.

Um ein Schnittstellen-Member explizit zu implementieren, stellen Sie dem Member-Namen den Namen der Schnittstelle voran und lassen Sie den Zugriffsmodifizierer weg:

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");
    }
}

Auf explizit implementierte Member kann nur über eine Variable des Schnittstellentyps zugegriffen werden, nicht über die Klasse selbst:

var adapter = new UniversalAdapter();
// adapter.Connect();  // Kompiliert nicht!

IAmericanPlug american = adapter;
american.Connect();  // Connected to 120V

IEuropeanPlug european = adapter;
european.Connect();  // Connected to 220V

Diese Technik ist nützlich für:

  • Auflösen von Namenskollisionen zwischen Schnittstellen-Membern
  • Verbergen von Schnittstellen-Membern vor der öffentlichen API der Klasse, wenn sie nur in einem bestimmten Schnittstellenkontext sinnvoll sind

Probier es selbst

using System;
using Reports;

class Program
{
    public static void Main(string[] args)
    {
        // Eingabe lesen
        string title = Console.ReadLine();
        string content = Console.ReadLine();

        // TODO: Erstelle ein Report-Objekt mit dem Titel und Inhalt

        // TODO: Speichere den Bericht in einer IDisplayable-Variable

        // TODO: Speichere denselben Bericht in einer IPrintable-Variable

        // TODO: Gib die Anzeigeversion aus, indem Render() auf IDisplayable aufgerufen wird
        Console.WriteLine("Display version:");
        // Gib das Render-Ergebnis hier aus

        // TODO: Gib die Druckversion aus, indem Render() auf IPrintable aufgerufen wird
        Console.WriteLine("Print version:");
        // Gib das Render-Ergebnis hier aus
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung