Menu
Coddy logo textTech

Interfaces Explicites

Fait partie de la section Programmation Orientée Objet du Journey C# de Coddy — leçon 28 sur 70.

Lorsqu'une classe implémente plusieurs interfaces qui possèdent des membres portant le même nom, vous êtes confronté à une collision de noms. L'implémentation explicite d'interface résout ce problème en vous permettant de fournir des implémentations distinctes pour chaque interface.

Avec une implémentation explicite, vous préfixez le nom du membre par le nom de l'interface et omettez le modificateur d'accès :

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

Les membres implémentés de manière explicite ne peuvent être accédés que via une variable du type de l'interface, et non via la classe elle-même :

var adapter = new UniversalAdapter();
// adapter.Connect();  // Ne compilera pas !

IAmericanPlug american = adapter;
american.Connect();  // Connecté au 120V

IEuropeanPlug european = adapter;
european.Connect();  // Connecté au 220V

Cette technique est également utile lorsque vous souhaitez masquer les membres de l'interface de l'API publique de la classe. Si une méthode n'a de sens que lorsque l'on traite l'objet comme une interface spécifique, l'implémentation explicite permet de garder la surface d'exposition de votre classe propre tout en respectant le contrat.

challenge icon

Défi

Facile

Construisons un système de génération de rapports qui démontre comment l'implémentation explicite d'interface permet à une seule classe de fournir des comportements différents selon l'interface utilisée. Vous allez créer un document qui peut être formaté différemment pour l'affichage par rapport à l'impression.

Vous organiserez votre code sur quatre fichiers :

  • IDisplayable.cs : Définissez une interface nommée IDisplayable dans l'espace de noms Reports. Cette interface représente un contenu qui peut être affiché à l'écran. Elle doit déclarer une méthode Render() qui retourne une chaîne de caractères (string).
  • IPrintable.cs : Définissez une interface nommée IPrintable dans l'espace de noms Reports. Cette interface représente un contenu qui peut être envoyé à une imprimante. Elle doit également déclarer une méthode Render() qui retourne une chaîne de caractères — remarquez que cela crée une collision de noms avec IDisplayable !
  • Report.cs : Définissez une classe Report dans l'espace de noms Reports qui implémente à la fois IDisplayable et IPrintable. Le rapport doit avoir une propriété Title (string) et une propriété Content (string), avec un constructeur qui définit ces deux valeurs. Utilisez l'implémentation explicite d'interface pour fournir différents comportements à Render() :
    • Lors du rendu pour l'affichage : "[SCREEN] {Title}: {Content}"
    • Lors du rendu pour l'impression : "[PRINT] {Title} | {Content}"
  • Program.cs : Dans votre fichier principal, créez un Report en utilisant les valeurs d'entrée. Étant donné que les implémentations explicites ne sont accessibles que via des variables d'interface, stockez le même rapport dans une variable IDisplayable et une variable IPrintable, puis appelez Render() sur chacune pour montrer comment le même objet produit des sorties différentes selon l'interface que vous utilisez.

Vous recevrez deux entrées :

  • Le titre du rapport
  • Le contenu du rapport

Affichez la sortie dans ce format :

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

Par exemple, si les entrées sont Sales Report et Q4 revenue increased by 15%, la sortie devrait être :

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

Remarquez comment le même objet Report se comporte différemment selon la référence d'interface que vous utilisez pour y accéder. C'est toute la puissance de l'implémentation explicite d'interface : résoudre les collisions de noms tout en fournissant un comportement approprié au contexte !

Aide-mémoire

Lorsqu'une classe implémente plusieurs interfaces avec des membres ayant le même nom, vous pouvez utiliser l'implémentation d'interface explicite pour fournir des implémentations distinctes pour chaque interface.

Pour implémenter explicitement un membre d'interface, préfixez le nom du membre avec le nom de l'interface et omettez le modificateur d'accès :

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

Les membres implémentés explicitement ne peuvent être accédés que via une variable du type de l'interface, et non via la classe elle-même :

var adapter = new UniversalAdapter();
// adapter.Connect();  // Won't compile!

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

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

Cette technique est utile pour :

  • Résoudre les collisions de noms entre les membres d'interface
  • Masquer les membres d'interface de l'API publique de la classe lorsqu'ils n'ont de sens que dans le contexte d'une interface spécifique

Essayez vous-même

using System;
using Reports;

class Program
{
    public static void Main(string[] args)
    {
        // Lire l'entrée
        string title = Console.ReadLine();
        string content = Console.ReadLine();

        // TODO: Créer un objet Report avec le titre et le contenu

        // TODO: Stocker le rapport dans une variable IDisplayable

        // TODO: Stocker le même rapport dans une variable IPrintable

        // TODO: Afficher la version d'affichage en appelant Render() sur IDisplayable
        Console.WriteLine("Display version:");
        // Afficher le résultat du rendu ici

        // TODO: Afficher la version d'impression en appelant Render() sur IPrintable
        Console.WriteLine("Print version:");
        // Afficher le résultat du rendu ici
    }
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet