Menu
Coddy logo textTech

Interfaces Explícitas

Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy — lección 28 de 70.

Cuando una clase implementa múltiples interfaces que tienen miembros con el mismo nombre, te enfrentas a una colisión de nombres. La implementación explícita de interfaces resuelve esto permitiéndote proporcionar implementaciones separadas para cada interfaz.

Con la implementación explícita, se antepone el nombre de la interfaz al nombre del miembro y se omite el modificador de acceso:

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

Los miembros implementados explícitamente solo pueden ser accedidos a través de una variable del tipo de interfaz, no a través de la clase misma:

var adapter = new UniversalAdapter();
// adapter.Connect();  // ¡No compilará!

IAmericanPlug american = adapter;
american.Connect();  // Conectado a 120V

IEuropeanPlug european = adapter;
european.Connect();  // Conectado a 220V

Esta técnica también es útil cuando desea ocultar miembros de la interfaz de la API pública de la clase. Si un método solo tiene sentido cuando se trata al objeto como una interfaz específica, la implementación explícita mantiene limpia el área de superficie de su clase mientras sigue cumpliendo con el contrato.

challenge icon

Desafío

Fácil

Vamos a construir un sistema generador de informes que demuestre cómo la implementación explícita de interfaces permite que una sola clase proporcione diferentes comportamientos dependiendo de qué interfaz se esté utilizando. Crearás un documento que puede formatearse de manera diferente para su visualización frente a su impresión.

Organizarás tu código en cuatro archivos:

  • IDisplayable.cs: Define una interfaz llamada IDisplayable en el espacio de nombres Reports. Esta interfaz representa contenido que se puede mostrar en pantalla. Debe declarar un método Render() que devuelva un string.
  • IPrintable.cs: Define una interfaz llamada IPrintable en el espacio de nombres Reports. Esta interfaz representa contenido que se puede enviar a una impresora. También debe declarar un método Render() que devuelva un string; ¡observa que esto crea una colisión de nombres con IDisplayable!
  • Report.cs: Define una clase Report en el espacio de nombres Reports que implemente tanto IDisplayable como IPrintable. El informe debe tener una propiedad Title (string) y una propiedad Content (string), con un constructor que establezca ambos valores. Utiliza la implementación explícita de interfaces para proporcionar diferentes comportamientos de Render():
    • Cuando se renderiza para visualización: "[SCREEN] {Title}: {Content}"
    • Cuando se renderiza para impresión: "[PRINT] {Title} | {Content}"
  • Program.cs: En tu archivo principal, crea un Report utilizando los valores de entrada. Dado que las implementaciones explícitas solo se pueden acceder a través de variables de interfaz, almacena el mismo informe tanto en una variable IDisplayable como en una variable IPrintable, luego llama a Render() en cada una para mostrar cómo el mismo objeto produce diferentes salidas según la interfaz que estés utilizando.

Recibirás dos entradas:

  • Título del informe
  • Contenido del informe

Imprime la salida en este formato:

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

Por ejemplo, si las entradas son Sales Report y Q4 revenue increased by 15%, la salida debería ser:

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

Observa cómo el mismo objeto Report se comporta de manera diferente dependiendo de qué referencia de interfaz utilices para acceder a él. ¡Este es el poder de la implementación explícita de interfaces: resolver colisiones de nombres mientras se proporciona un comportamiento apropiado al contexto!

Hoja de referencia

Cuando una clase implementa múltiples interfaces con miembros que tienen el mismo nombre, puedes usar la implementación explícita de interfaz para proporcionar implementaciones separadas para cada interfaz.

Para implementar explícitamente un miembro de interfaz, antepón el nombre de la interfaz al nombre del miembro y omite el modificador de acceso:

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

Solo se puede acceder a los miembros implementados explícitamente a través de una variable del tipo de interfaz, no a través de la propia clase:

var adapter = new UniversalAdapter();
// adapter.Connect();  // ¡No compilará!

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

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

Esta técnica es útil para:

  • Resolver colisiones de nombres entre miembros de interfaz
  • Ocultar miembros de interfaz de la API pública de la clase cuando solo tienen sentido en el contexto de una interfaz específica

Pruébalo tú mismo

using System;
using Reports;

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

        // TODO: Crea un objeto Report con el title y content

        // TODO: Almacena el informe en una variable IDisplayable

        // TODO: Almacena el mismo informe en una variable IPrintable

        // TODO: Imprime la versión de visualización llamando a Render() en IDisplayable
        Console.WriteLine("Display version:");
        // Imprime el resultado del renderizado aquí

        // TODO: Imprime la versión de impresión llamando a Render() en IPrintable
        Console.WriteLine("Print version:");
        // Imprime el resultado del renderizado aquí
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos