Implementar interfaces en C# te permite definir un contrato que cualquier clase puede cumplir, sin exponer sus métodos internos. Aquí verás cómo aplicarlo en un proyecto de superhéroes y antihéroes usando Visual Studio, paso a paso, con un caso real que cualquier desarrollador junior puede replicar.
¿Por qué usar una interfaz en lugar de pasar la clase completa?
Cuando necesitas que un método reciba datos generales pero no quieres que ese método pueda ejecutar las funciones internas de la clase, una interface es la solución natural. En el proyecto se construye un método genérico para imprimir la información del personaje, y aquí aparece la primera ventaja del contrato: garantizar que lleguen el ID, el nombre y la identidad secreta sin importar si el objeto es un superhéroe, un antihéroe o cualquier otro personaje futuro [00:35].
Al pasar la clase directamente, el componente receptor podría ejecutar funciones que no le corresponden. Con la interfaz solo expones lo necesario.
¿Qué es una interfaz en C#? Es un contrato que define propiedades y métodos que una clase debe implementar, sin contener lógica propia. Sirve para garantizar comportamiento sin acoplar implementaciones.
¿Cómo se crea y declara una interfaz en Visual Studio?
El primer paso es crear una carpeta llamada Interfaces y dentro un nuevo archivo con el prefijo I, en este caso ISuperheroe [02:05]. Visual Studio detecta el prefijo y genera automáticamente la estructura usando la palabra reservada interface en lugar de class.
Dentro de la interfaz no se usan modificadores de acceso, porque no contiene implementación. Solo se declaran las propiedades con get y set:
csharp
public interface ISuperheroe
{
int Id { get; set; }
string Nombre { get; set; }
string IdentidadSecreta { get; set; }
}
Esas tres propiedades son el contrato mínimo que cualquier clase deberá cumplir para ser tratada como ISuperheroe.
¿Cómo implementar una interfaz cuando ya hay herencia de por medio?
La clase Superheroe ya hereda de Heroe, así que se agrega la interfaz separada por coma después de la clase base:
csharp
public class Superheroe : Heroe, ISuperheroe
Al hacerlo, Visual Studio marca un error si las propiedades del contrato no están correctamente expuestas con get y set. En el ejercicio, las propiedades Id e IdentidadSecreta no tenían el accesor automático, así que se ajustan a { get; set; } y el error desaparece [04:30]. La propiedad Nombre no marca error porque ya tiene encapsulamiento personalizado, y eso también cumple el contrato.
¿Cómo construir la utilidad que consume la interfaz?
En la clase Program no tiene sentido escribir el método de impresión, así que se crea una clase auxiliar llamada ImprimirInfo con un método ImprimirSuperheroe que recibe como parámetro un ISuperheroe [06:10].
csharp
public class ImprimirInfo
{
public void ImprimirSuperheroe(ISuperheroe superheroe)
{
Console.WriteLine($"ID: {superheroe.Id}");
Console.WriteLine($"Nombre: {superheroe.Nombre}");
Console.WriteLine($"Identidad secreta: {superheroe.IdentidadSecreta}");
}
}
El método usa string interpolation para imprimir cada propiedad del contrato. No conoce ni necesita conocer si el objeto es Superman, Wolverine o cualquier otro.
¿Cómo invocar la utilidad desde Program?
Desde Program se instancia el objeto con la palabra clave new y se le pasa el superhéroe creado previamente:
csharp
ImprimirInfo imprimir = new ImprimirInfo();
imprimir.ImprimirSuperheroe(superman);
Al ejecutar, la consola muestra el ID, el nombre y la identidad secreta de Superman, seguido de los demás métodos del programa [07:50].
¿Por qué funciona también con un antihéroe sin cambiar nada?
Aquí está la parte interesante. La clase Antiheroe hereda de Superheroe, y como Superheroe implementa ISuperheroe, la herencia transmite ese contrato automáticamente. Por eso al pasar a Wolverine al mismo método, todo funciona sin modificar una sola línea [08:40].
csharp
imprimir.ImprimirSuperheroe(wolverine);
Esto demuestra el verdadero poder del polimorfismo basado en interfaces: el método trata a todos los personajes como ISuperheroe, sin importar su tipo concreto.
¿Cuándo conviene usar una interfaz en lugar de pasar la clase? Cuando solo necesitas exponer ciertos datos o comportamientos y quieres evitar que el consumidor acceda a métodos internos de la clase original.
¿Qué pasa si una clase hija ya hereda de otra clase? Puedes implementar la interfaz agregándola con coma después de la clase base, ya que C# permite herencia simple pero múltiples interfaces.
¿Qué diferencia hay entre una interfaz y una clase abstracta?
Al ver el comportamiento del contrato, surge la duda: las interfaces se parecen mucho a las clases abstractas. Ambas definen estructuras que otras clases deben cumplir, pero hay diferencias importantes en cuándo usar cada una, y eso se profundiza en la siguiente lección.
Si ya replicaste el ejercicio, cuéntame en los comentarios cómo aplicarías una interfaz en tu propio proyecto y qué clases adaptarías para cumplir un contrato común.