CSharp

Tutorial CSharp: Polimorfismo

Aprende el polimorfismo en C# y cómo usar casting seguro con operadores is y as para un código orientado a objetos robusto y flexible.

Aprende CSharp y certifícate

Polimorfismo en acción

El polimorfismo es uno de los pilares fundamentales de la programación orientada a objetos que permite tratar objetos de diferentes clases a través de una interfaz común. En C#, el polimorfismo nos permite trabajar con objetos de clases derivadas a través de referencias a sus clases base.

Para entender el polimorfismo en acción, imaginemos un escenario práctico. Supongamos que estamos desarrollando un sistema para una tienda de mascotas que maneja diferentes tipos de animales. Cada animal tiene comportamientos comunes, pero los implementa de manera diferente.

Primero, definamos una clase base Animal con un método virtual:

public class Animal
{
    public string Nombre { get; set; }
    
    public Animal(string nombre)
    {
        Nombre = nombre;
    }
    
    // Método virtual que puede ser sobrescrito por clases derivadas
    public virtual string HacerSonido()
    {
        return "...";
    }
    
    public virtual void Alimentar()
    {
        Console.WriteLine($"Alimentando a {Nombre}");
    }
}

Ahora, creemos algunas clases derivadas que sobrescriban estos métodos:

public class Perro : Animal
{
    public Perro(string nombre) : base(nombre) { }
    
    // Sobrescribimos el método de la clase base
    public override string HacerSonido()
    {
        return "¡Guau!";
    }
    
    public override void Alimentar()
    {
        Console.WriteLine($"Dando croquetas a {Nombre}");
    }
    
    // Método específico de la clase Perro
    public void Pasear()
    {
        Console.WriteLine($"Paseando a {Nombre}");
    }
}

public class Gato : Animal
{
    public Gato(string nombre) : base(nombre) { }
    
    public override string HacerSonido()
    {
        return "¡Miau!";
    }
    
    public override void Alimentar()
    {
        Console.WriteLine($"Sirviendo atún a {Nombre}");
    }
    
    // Método específico de la clase Gato
    public void Acicalar()
    {
        Console.WriteLine($"Acicalando a {Nombre}");
    }
}

Ahora veamos el polimorfismo en acción. Podemos crear una lista de animales que contenga tanto perros como gatos, y tratarlos de manera uniforme a través de la interfaz común de la clase Animal:

static void Main(string[] args)
{
    // Creamos una lista de tipo Animal
    List<Animal> animales = new List<Animal>
    {
        new Perro("Bobby"),
        new Gato("Whiskers"),
        new Perro("Rex"),
        new Gato("Mittens")
    };
    
    // Iteramos sobre la lista y llamamos a los métodos polimórficos
    foreach (Animal animal in animales)
    {
        Console.WriteLine($"{animal.Nombre} dice: {animal.HacerSonido()}");
        animal.Alimentar();
        Console.WriteLine();
    }
}

La salida del programa sería:

Bobby dice: ¡Guau!
Dando croquetas a Bobby

Whiskers dice: ¡Miau!
Sirviendo atún a Whiskers

Rex dice: ¡Guau!
Dando croquetas a Rex

Mittens dice: ¡Miau!
Sirviendo atún a Mittens

Esto demuestra el polimorfismo en acción: aunque estamos tratando todos los objetos como Animal, cada uno ejecuta su propia implementación de los métodos HacerSonido() y Alimentar(). C# determina en tiempo de ejecución qué método debe invocar basándose en el tipo real del objeto, no en el tipo de la referencia.

Veamos otro ejemplo práctico con una jerarquía de formas geométricas:

public abstract class Forma
{
    // Método abstracto que debe ser implementado por todas las clases derivadas
    public abstract double CalcularArea();
    
    // Método virtual con implementación por defecto
    public virtual void Dibujar()
    {
        Console.WriteLine("Dibujando una forma");
    }
}

public class Circulo : Forma
{
    public double Radio { get; set; }
    
    public Circulo(double radio)
    {
        Radio = radio;
    }
    
    public override double CalcularArea()
    {
        return Math.PI * Radio * Radio;
    }
    
    public override void Dibujar()
    {
        Console.WriteLine($"Dibujando un círculo con radio {Radio}");
    }
}

public class Rectangulo : Forma
{
    public double Ancho { get; set; }
    public double Alto { get; set; }
    
    public Rectangulo(double ancho, double alto)
    {
        Ancho = ancho;
        Alto = alto;
    }
    
    public override double CalcularArea()
    {
        return Ancho * Alto;
    }
    
    public override void Dibujar()
    {
        Console.WriteLine($"Dibujando un rectángulo de {Ancho}x{Alto}");
    }
}

Ahora podemos usar el polimorfismo para procesar diferentes formas de manera uniforme:

static void ProcesarFormas()
{
    Forma[] formas = new Forma[]
    {
        new Circulo(5),
        new Rectangulo(4, 6),
        new Circulo(3)
    };
    
    foreach (Forma forma in formas)
    {
        // Llamamos a métodos polimórficos
        forma.Dibujar();
        Console.WriteLine($"Área: {forma.CalcularArea():F2}");
        Console.WriteLine();
    }
}

La salida sería:

Dibujando un círculo con radio 5
Área: 78.54

Dibujando un rectángulo de 4x6
Área: 24.00

Dibujando un círculo con radio 3
Área: 28.27

El polimorfismo también se aplica cuando pasamos objetos como parámetros a métodos. Por ejemplo:

static void MostrarInformacion(Forma forma)
{
    Console.WriteLine($"Tipo: {forma.GetType().Name}");
    Console.WriteLine($"Área: {forma.CalcularArea():F2}");
    forma.Dibujar();
}

// Uso:
MostrarInformacion(new Circulo(10));  // Pasamos un Circulo como Forma
MostrarInformacion(new Rectangulo(5, 8));  // Pasamos un Rectangulo como Forma

El polimorfismo en acción nos permite escribir código más flexible y extensible. Podemos agregar nuevas clases derivadas sin modificar el código existente que trabaja con la clase base. Por ejemplo, si añadimos una nueva clase Triangulo que herede de Forma, todo nuestro código que procesa formas funcionará automáticamente con triángulos.

public class Triangulo : Forma
{
    public double Base { get; set; }
    public double Altura { get; set; }
    
    public Triangulo(double baseTriangulo, double altura)
    {
        Base = baseTriangulo;
        Altura = altura;
    }
    
    public override double CalcularArea()
    {
        return (Base * Altura) / 2;
    }
    
    public override void Dibujar()
    {
        Console.WriteLine($"Dibujando un triángulo con base {Base} y altura {Altura}");
    }
}

Ahora podemos incluir triángulos en nuestro array de formas sin cambiar el código que procesa las formas:

Forma[] formas = new Forma[]
{
    new Circulo(5),
    new Rectangulo(4, 6),
    new Triangulo(8, 3)
};

// El mismo código funciona con la nueva clase
foreach (Forma forma in formas)
{
    forma.Dibujar();
    Console.WriteLine($"Área: {forma.CalcularArea():F2}");
}

Esta capacidad de tratar objetos de diferentes clases a través de una interfaz común es la esencia del polimorfismo en acción y es una de las características más potentes de la programación orientada a objetos en C#.

Casting seguro

Cuando trabajamos con polimorfismo en C#, a menudo necesitamos convertir objetos entre tipos relacionados en la jerarquía de clases. Aunque podemos usar referencias de tipo base para manipular objetos de tipos derivados, en ocasiones necesitamos acceder a miembros específicos de la clase derivada. Para esto utilizamos el casting o conversión de tipos.

El casting tradicional en C# se realiza mediante paréntesis, pero puede generar excepciones si la conversión no es válida. Por ejemplo:

Animal miAnimal = new Perro("Fido");
// Casting tradicional - peligroso si miAnimal no es realmente un Perro
Perro miPerro = (Perro)miAnimal; // Funciona porque miAnimal realmente es un Perro

Sin embargo, si intentamos hacer un casting incorrecto, obtendremos una excepción:

Animal miAnimal = new Gato("Felix");
// Esto lanzará una InvalidCastException en tiempo de ejecución
Perro miPerro = (Perro)miAnimal; // Error: no se puede convertir un Gato en Perro

Para evitar estas excepciones, C# proporciona mecanismos de casting seguro que nos permiten verificar la compatibilidad de tipos antes de realizar la conversión.

Operador de conversión segura: as

El operador as intenta realizar una conversión entre tipos compatibles. Si la conversión no es posible, devuelve null en lugar de lanzar una excepción:

Animal miAnimal = new Gato("Felix");
// Intenta convertir a Perro, pero devuelve null si no es posible
Perro miPerro = miAnimal as Perro; // miPerro será null

// Verificamos antes de usar
if (miPerro != null)
{
    miPerro.Pasear(); // Solo se ejecuta si la conversión fue exitosa
}
else
{
    Console.WriteLine("El animal no es un perro");
}

Este enfoque es especialmente útil cuando no estamos seguros del tipo exacto del objeto y queremos evitar excepciones:

void ProcesarAnimal(Animal animal)
{
    // Intentamos convertir de forma segura
    Perro perro = animal as Perro;
    if (perro != null)
    {
        // Podemos usar métodos específicos de Perro
        perro.Pasear();
        return;
    }
    
    Gato gato = animal as Gato;
    if (gato != null)
    {
        // Podemos usar métodos específicos de Gato
        gato.Acicalar();
        return;
    }
    
    // Si llegamos aquí, el animal no es ni perro ni gato
    Console.WriteLine("Animal de tipo desconocido");
}

Es importante recordar que el operador as solo funciona con tipos de referencia y tipos anulables. No puede usarse con tipos de valor a menos que sean anulables.

Comprobación de tipos con pattern matching

C# moderno ofrece una sintaxis más elegante para el casting seguro mediante pattern matching:

void ProcesarAnimalModerno(Animal animal)
{
    // Usando pattern matching para casting y comprobación en una sola operación
    if (animal is Perro perro)
    {
        // La variable 'perro' está disponible en este ámbito
        Console.WriteLine($"{perro.Nombre} es un perro");
        perro.Pasear();
    }
    else if (animal is Gato gato)
    {
        Console.WriteLine($"{gato.Nombre} es un gato");
        gato.Acicalar();
    }
    else
    {
        Console.WriteLine("Tipo de animal desconocido");
    }
}

Esta sintaxis combina la comprobación de tipo y la conversión en una sola operación, lo que hace que el código sea más limpio y menos propenso a errores.

Casting seguro con switch expressions

Para casos más complejos, podemos utilizar las expresiones switch con pattern matching:

string DescribirAnimal(Animal animal)
{
    return animal switch
    {
        Perro perro => $"{perro.Nombre} es un perro que hace {perro.HacerSonido()}",
        Gato gato => $"{gato.Nombre} es un gato que hace {gato.HacerSonido()}",
        _ => $"Animal desconocido llamado {animal.Nombre}"
    };
}

Este enfoque es particularmente útil cuando necesitamos realizar diferentes acciones basadas en el tipo del objeto.

Ejemplo práctico de casting seguro

Veamos un ejemplo completo que demuestra varias técnicas de casting seguro:

public class GestionAnimales
{
    public void ProcesarListaAnimales(List<Animal> animales)
    {
        foreach (var animal in animales)
        {
            // Método 1: Usando 'as' con verificación null
            Perro perroConAs = animal as Perro;
            if (perroConAs != null)
            {
                Console.WriteLine($"Método 1: {perroConAs.Nombre} es un perro");
                continue; // Pasamos al siguiente animal
            }
            
            // Método 2: Usando pattern matching con 'is'
            if (animal is Gato gatoConIs)
            {
                Console.WriteLine($"Método 2: {gatoConIs.Nombre} es un gato");
                continue;
            }
            
            // Método 3: Usando switch con pattern matching
            switch (animal)
            {
                case Perro p when p.Nombre.StartsWith("R"):
                    Console.WriteLine($"Método 3: {p.Nombre} es un perro cuyo nombre empieza con R");
                    break;
                case Gato g when g.Nombre.Length > 5:
                    Console.WriteLine($"Método 3: {g.Nombre} es un gato con nombre largo");
                    break;
                default:
                    Console.WriteLine($"Método 3: {animal.Nombre} es un animal de tipo {animal.GetType().Name}");
                    break;
            }
        }
    }
}

Consideraciones de rendimiento

Al elegir entre diferentes métodos de casting seguro, es importante considerar el rendimiento:

  • El operador as es generalmente más rápido que una combinación de is seguido de un casting tradicional, ya que solo realiza la comprobación de tipo una vez.
  • El pattern matching con is es más legible y seguro, aunque puede ser ligeramente menos eficiente en algunos casos.
  • Para múltiples comprobaciones de tipo en el mismo objeto, el switch con pattern matching suele ser la opción más eficiente y legible.

Casting seguro con tipos genéricos

También podemos implementar métodos genéricos para realizar casting seguro:

public T CastSeguro<T>(Animal animal) where T : Animal
{
    if (animal is T resultado)
    {
        return resultado;
    }
    return null; // O lanzar una excepción, según el caso de uso
}

// Uso:
Perro perro = CastSeguro<Perro>(miAnimal);
if (perro != null)
{
    perro.Pasear();
}

Este enfoque es útil cuando necesitamos realizar el mismo tipo de casting en múltiples lugares del código.

El casting seguro es una herramienta fundamental cuando trabajamos con jerarquías de clases y polimorfismo en C#, permitiéndonos aprovechar la flexibilidad del polimorfismo mientras mantenemos la seguridad de tipos y evitamos excepciones en tiempo de ejecución.

Operadores is y as

En C#, los operadores is y as son herramientas fundamentales para trabajar con el polimorfismo y realizar comprobaciones y conversiones de tipos de manera segura. Estos operadores nos permiten interactuar con la jerarquía de clases sin arriesgar excepciones en tiempo de ejecución.

El operador is

El operador is evalúa si un objeto es compatible con un tipo específico, devolviendo un valor booleano (true o false) como resultado. Este operador no realiza ninguna conversión, simplemente verifica la compatibilidad de tipos.

La sintaxis básica es:

if (objeto is TipoDestino)
{
    // El objeto es compatible con TipoDestino
}

Por ejemplo, podemos verificar si un objeto de tipo Animal es realmente un Perro:

Animal animal = new Perro("Rex");

if (animal is Perro)
{
    Console.WriteLine("El animal es un perro");
}

El operador is es especialmente útil cuando necesitamos realizar diferentes acciones según el tipo real del objeto:

void RealizarAccionEspecifica(Animal animal)
{
    if (animal is Perro)
    {
        Console.WriteLine("Procesando un perro");
        // Aquí necesitaríamos hacer un casting para acceder a miembros específicos
    }
    else if (animal is Gato)
    {
        Console.WriteLine("Procesando un gato");
    }
    else
    {
        Console.WriteLine("Animal genérico");
    }
}

Pattern matching con is

A partir de C# 7.0, el operador is se mejoró con pattern matching, permitiendo declarar una variable del tipo destino en la misma expresión:

if (animal is Perro perro)
{
    // La variable 'perro' está disponible aquí y es del tipo Perro
    Console.WriteLine($"El perro se llama {perro.Nombre}");
    perro.Pasear(); // Podemos llamar a métodos específicos de Perro
}

Esta sintaxis combina la comprobación de tipo y la asignación en una sola operación, lo que hace el código más conciso y legible. La variable declarada (perro en este caso) solo está disponible dentro del bloque if y solo se asigna si la comprobación es exitosa.

También podemos usar condiciones adicionales con pattern matching:

if (animal is Perro perro && perro.Nombre.StartsWith("R"))
{
    Console.WriteLine($"Es un perro cuyo nombre empieza por R: {perro.Nombre}");
}

El operador as

El operador as intenta convertir un objeto a un tipo específico, pero a diferencia del casting tradicional, devuelve null si la conversión no es posible en lugar de lanzar una excepción.

La sintaxis básica es:

TipoDestino variable = objeto as TipoDestino;

Por ejemplo:

Animal animal = new Gato("Whiskers");

// Intenta convertir a Perro
Perro perro = animal as Perro; // perro será null porque animal es un Gato

// Verificamos antes de usar
if (perro != null)
{
    perro.Pasear();
}
else
{
    Console.WriteLine("No es un perro");
}

El operador as solo funciona con:

  • Tipos de referencia (clases)
  • Tipos anulables (como int?, bool?, etc.)

No puede usarse con tipos de valor no anulables:

// Esto NO compila
int numero = objeto as int; // Error

// Esto SÍ compila
int? numeroNulable = objeto as int?; // Correcto

Comparación entre is y as

Aunque ambos operadores se utilizan para trabajar con tipos de manera segura, tienen propósitos diferentes:

  • is: Verifica si un objeto es compatible con un tipo (devuelve bool)
  • as: Intenta convertir un objeto a un tipo (devuelve el objeto convertido o null)

Veamos un ejemplo comparativo:

// Enfoque con 'is' (requiere casting adicional)
if (animal is Perro)
{
    Perro perro = (Perro)animal; // Casting tradicional
    perro.Pasear();
}

// Enfoque con 'is' y pattern matching (más moderno)
if (animal is Perro perro)
{
    perro.Pasear();
}

// Enfoque con 'as'
Perro perro = animal as Perro;
if (perro != null)
{
    perro.Pasear();
}

Escenarios de uso

Cuando usar is:

  • Cuando solo necesitas verificar el tipo sin necesariamente acceder a sus miembros específicos
  • Cuando quieres combinar la verificación de tipo con otras condiciones
  • Con pattern matching, cuando necesitas verificar y convertir en una sola operación
// Verificación simple
bool esPerro = animal is Perro;

// Con pattern matching y condiciones adicionales
if (animal is Perro p && p.Edad > 5)
{
    Console.WriteLine("Es un perro adulto");
}

Cuando usar as:

  • Cuando necesitas convertir un objeto para acceder a sus miembros específicos
  • Cuando la conversión podría fallar y prefieres manejar el caso null en lugar de una excepción
  • Cuando planeas usar el objeto convertido en múltiples lugares
Perro perro = animal as Perro;
if (perro != null)
{
    // Usamos perro en múltiples operaciones
    perro.Pasear();
    perro.Alimentar();
    Console.WriteLine(perro.HacerSonido());
}

Ejemplo práctico: Sistema de notificaciones

Veamos un ejemplo práctico donde usamos ambos operadores en un sistema de notificaciones:

public abstract class Notificacion
{
    public string Mensaje { get; set; }
    public DateTime Fecha { get; set; }
    
    public abstract void Mostrar();
}

public class NotificacionEmail : Notificacion
{
    public string DireccionEmail { get; set; }
    
    public override void Mostrar()
    {
        Console.WriteLine($"Email a {DireccionEmail}: {Mensaje}");
    }
    
    public void Reenviar(string nuevaDireccion)
    {
        Console.WriteLine($"Reenviando email a {nuevaDireccion}");
    }
}

public class NotificacionSMS : Notificacion
{
    public string NumeroTelefono { get; set; }
    
    public override void Mostrar()
    {
        Console.WriteLine($"SMS a {NumeroTelefono}: {Mensaje}");
    }
    
    public void EnviarRecordatorio()
    {
        Console.WriteLine($"Enviando recordatorio a {NumeroTelefono}");
    }
}

Ahora, implementemos un procesador de notificaciones que utilice los operadores is y as:

public class ProcesadorNotificaciones
{
    public void Procesar(List<Notificacion> notificaciones)
    {
        foreach (var notificacion in notificaciones)
        {
            // Primero mostramos todas las notificaciones (polimorfismo)
            notificacion.Mostrar();
            
            // Usando 'is' con pattern matching
            if (notificacion is NotificacionEmail email && email.DireccionEmail.EndsWith("@empresa.com"))
            {
                Console.WriteLine("Notificación interna de la empresa");
                email.Reenviar("archivos@empresa.com");
            }
            
            // Usando 'as'
            NotificacionSMS sms = notificacion as NotificacionSMS;
            if (sms != null && sms.NumeroTelefono.StartsWith("+34"))
            {
                Console.WriteLine("Notificación a número español");
                sms.EnviarRecordatorio();
            }
        }
    }
}

Uso con interfaces

Los operadores is y as también funcionan con interfaces, lo que los hace muy útiles para trabajar con sistemas basados en interfaces:

public interface IImprimible
{
    void Imprimir();
}

public class Documento : IImprimible
{
    public string Titulo { get; set; }
    
    public void Imprimir()
    {
        Console.WriteLine($"Imprimiendo documento: {Titulo}");
    }
    
    public void GuardarPDF()
    {
        Console.WriteLine("Guardando como PDF");
    }
}

// Uso:
object obj = new Documento { Titulo = "Informe Anual" };

// Verificando si implementa la interfaz
if (obj is IImprimible imprimible)
{
    imprimible.Imprimir();
}

// Convertir a la clase concreta
Documento doc = obj as Documento;
if (doc != null)
{
    doc.GuardarPDF();
}

Rendimiento y buenas prácticas

Al trabajar con los operadores is y as, considera estas recomendaciones:

  • Evita verificaciones redundantes: Si ya has usado is para verificar un tipo, no uses as inmediatamente después para la misma conversión.
// Mal (redundante)
if (animal is Perro)
{
    Perro perro = animal as Perro; // Innecesario, ya sabemos que es un Perro
    perro.Pasear();
}

// Bien (con pattern matching)
if (animal is Perro perro)
{
    perro.Pasear();
}
  • Prefiere pattern matching: En código moderno, el pattern matching con is suele ser más legible que usar as seguido de una verificación de nulidad.

  • Considera el rendimiento: Para múltiples verificaciones de tipo en el mismo objeto, considera usar switch con pattern matching:

switch (animal)
{
    case Perro perro:
        perro.Pasear();
        break;
    case Gato gato:
        gato.Acicalar();
        break;
    default:
        Console.WriteLine("Animal desconocido");
        break;
}

Los operadores is y as son herramientas esenciales para trabajar con jerarquías de clases en C#, permitiéndonos aprovechar el polimorfismo mientras mantenemos un código seguro y robusto.

Aprende CSharp online

Otros ejercicios de programación de CSharp

Evalúa tus conocimientos de esta lección Polimorfismo con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Todas las lecciones de CSharp

Accede a todas las lecciones de CSharp y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A C#

Introducción Y Entorno

Creación De Proyecto C#

Introducción Y Entorno

Variables Y Constantes

Sintaxis

Tipos De Datos

Sintaxis

Operadores

Sintaxis

Control De Flujo

Sintaxis

Funciones

Sintaxis

Estructuras De Control Iterativo

Sintaxis

Interpolación De Strings

Sintaxis

Estructuras De Control Condicional

Sintaxis

Manejo De Valores Nulos

Sintaxis

Clases Y Encapsulación

Programación Orientada A Objetos

Objetos

Programación Orientada A Objetos

Constructores Y Destructores

Programación Orientada A Objetos

Herencia

Programación Orientada A Objetos

Polimorfismo

Programación Orientada A Objetos

Genéricos

Programación Orientada A Objetos

Métodos Virtuales Y Sobrecarga

Programación Orientada A Objetos

Clases Abstractas

Programación Orientada A Objetos

Interfaces

Programación Orientada A Objetos

Propiedades Y Encapsulación

Programación Orientada A Objetos

Métodos De Extensión

Programación Orientada A Objetos

Clases Y Objetos

Programación Orientada A Objetos

Clases Parciales

Programación Orientada A Objetos

Miembros Estáticos

Programación Orientada A Objetos

Tuplas Y Tipos Anónimos

Programación Orientada A Objetos

Arrays Y Listas

Colecciones Y Linq

Diccionarios

Colecciones Y Linq

Conjuntos, Colas Y Pilas

Colecciones Y Linq

Uso De Consultas Linq

Colecciones Y Linq

Linq Avanzado

Colecciones Y Linq

Colas Y Pilas

Colecciones Y Linq

Conjuntos

Colecciones Y Linq

Linq Básico

Colecciones Y Linq

Delegados Funcionales

Programación Funcional

Records

Programación Funcional

Expresiones Lambda

Programación Funcional

Linq Funcional

Programación Funcional

Fundamentos De La Programación Funcional

Programación Funcional

Pattern Matching

Programación Funcional

Testing Unitario Con Xunit

Testing

Excepciones

Excepciones

Delegados

Programación Asíncrona

Eventos

Programación Asíncrona

Lambdas

Programación Asíncrona

Uso De Async Y Await

Programación Asíncrona

Tareas

Programación Asíncrona

Accede GRATIS a CSharp y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Comprender el concepto de polimorfismo y su implementación en C# mediante métodos virtuales y abstractos.
  • Aprender a utilizar el polimorfismo para tratar objetos de diferentes clases derivadas a través de una interfaz común.
  • Conocer las técnicas de casting seguro en C#, incluyendo el uso de los operadores as y is.
  • Aplicar pattern matching para realizar conversiones y comprobaciones de tipo de forma más legible y segura.
  • Evaluar buenas prácticas y consideraciones de rendimiento al trabajar con casting y polimorfismo en C#.