Invalidar propiedades y métodos en una clase derivada

Completado

En C#, puede usar la palabra clave override para ampliar o modificar el comportamiento de la clase base en la clase derivada. La palabra clave override permite invalidar propiedades y métodos que se heredan de la clase base y proporcionar implementaciones personalizadas en la clase derivada. Esto le permite reutilizar el código definido en la clase base y ampliar o modificar el comportamiento de la clase base en la clase derivada.

Para invalidar una propiedad o un método en una clase derivada, debe seguir estos pasos:

  1. Declare los miembros de la clase base como abstract o virtual.
  2. Invalide los miembros de la clase derivada.
  3. Opcionalmente, use la palabra clave base para tener acceso a la implementación de clase base desde el miembro invalidado en la clase derivada.

Declarar miembros abstractos y virtuales de la clase base

Para poder invalidar los miembros de una clase derivada, debe declarar los miembros de la clase base como abstract o virtual.

  • Abstract: la palabra clave abstract indica que el miembro no tiene ninguna implementación y se debe invalidar en una clase derivada.
  • Virtual: la palabra clave virtual indica que el miembro tiene una implementación, pero se puede invalidar o extender en una clase derivada.

En el ejemplo siguiente se muestra cómo declarar miembros abstractos y virtuales en una clase base:


public class BaseClass
{
    public string Property1 { get; set; } = "Base - Property1";
    public string Property2 { get; set; } = "Base - Property2";

    public virtual void Method1()
    {
        Console.WriteLine("Base - Method1");
    }

    public void Method2()
    {
        Console.WriteLine("Base - Method2");
    }
}

Invalidar miembros en la clase derivada

Después de declarar los miembros de la clase base como abstract o virtual, puede invalidar los miembros de la clase derivada mediante la palabra clave override. La palabra clave override indica que el miembro de la clase derivada reemplaza al miembro de la clase base.

Tenga en cuenta el ejemplo siguiente:


// Step 1: Create instances of the base class and the derived classes
BaseClass baseClass = new BaseClass();
DerivedClass derivedClass = new DerivedClass();
BaseClass baseClassReferencingDerivedClass = new DerivedClass();

// Step 2: Access properties and methods of the base class
Console.WriteLine($"\n{baseClass.Property1}");
Console.WriteLine($"{baseClass.Property2}");
baseClass.Method1();
baseClass.Method2();

// Step 3: Access properties and methods of the derived class
Console.WriteLine($"\n{derivedClass.Property1}");
Console.WriteLine($"{derivedClass.Property2}");
derivedClass.Method1();
derivedClass.Method2();

// Step 4: Access properties and methods of the base class that references the derived class
Console.WriteLine($"\n{baseClassReferencingDerivedClass.Property1}");
Console.WriteLine($"{baseClassReferencingDerivedClass.Property2}");
baseClassReferencingDerivedClass.Method1();
baseClassReferencingDerivedClass.Method2();

/*Output:
Base - Property1
Base - Property2
Base - Method1
Base - Method2

Derived - Property1
Derived - Property2
Derived - Method1
Derived - Method2

Derived - Property1
Base - Property2
Derived - Method1
Base - Method2
*/

public class BaseClass
{
    public virtual string Property1 { get; set; } = "Base - Property1";
    public virtual string Property2 { get; set; } = "Base - Property2";

    public virtual void Method1()
    {
        Console.WriteLine("Base - Method1");
    }

    public void Method2()
    {
        Console.WriteLine("Base - Method2");
    }
}

public class DerivedClass : BaseClass
{
    public override string Property1 { get; set; } = "Derived - Property1";
    public new string Property2 { get; set; } = "Derived - Property2";

    public override void Method1()
    {
        Console.WriteLine("Derived - Method1");
    }

    public new void Method2()
    {
        Console.WriteLine("Derived - Method2");
    }
}

En este código se muestra cómo modificar y ocultar propiedades y métodos en una clase derivada mediante las palabras clave override y new. El código define una clase base (BaseClass) y una clase derivada (DerivedClass) que hereda de la clase base. La clase base tiene dos propiedades (Property1 y Property2) y dos métodos (Method1 y Method2). La clase derivada invalida Property1 y Method1 y oculta Property2 y Method2 mediante la palabra clave new.

Este es un desglose del código:

  1. En el paso 1, el código crea una instancia de la clase base (baseClass), una instancia de la clase derivada (derivedClass) y una instancia de la clase derivada a la que hace referencia una variable de clase base (baseClassReferencingDerivedClass). La instancia de baseClassReferencingDerivedClass muestra el polimorfismo, donde una referencia de clase base apunta a un objeto de clase derivada.

  2. En el paso 2, el código accede e imprime las propiedades y métodos de la instancia de clase base. Dado que baseClass es una instancia de BaseClass, usa las propiedades y los métodos definidos en BaseClass. La salida muestra los valores y comportamientos definidos en la clase base.

  3. En el paso 3, el código accede e imprime las propiedades y los métodos de la instancia de clase derivada. Dado que derivedClass es una instancia de DerivedClass, usa las propiedades y métodos invalidados definidos en DerivedClass. La salida refleja los valores y comportamientos definidos en la clase derivada.

  4. En el paso 4, el código accede a las propiedades y métodos de la instancia de clase derivada a la que hace referencia una variable de clase base. Para propiedades y métodos que se invalidan (Property1 y Method1), se usa la implementación de clase derivada. Sin embargo, para propiedades y métodos ocultos (Property2 y Method2), se usa la implementación de clase base. Esto muestra la diferencia entre la invalidación del método (mediante override) y la ocultación de métodos (mediante new).

Definición de una clase base abstracta

Cuando se define una clase base como abstract, se indica que la clase está incompleta y debe implementarse mediante clases derivadas. Las clases abstractas pueden contener propiedades abstractas y métodos que deben implementarse mediante clases derivadas. Las clases base abstractas no se pueden crear instancias directamente, por lo que se usan como plantilla para las clases derivadas para implementar los miembros abstractos.

Tenga en cuenta el ejemplo siguiente:


// create instances of the base class and the derived classes
// Note: We can't create an instance of an abstract class
// BaseClass baseClass = new BaseClass(); // This line would cause a compile-time error
DerivedClass derivedClass = new DerivedClass();
BaseClass baseClassReferencingDerivedClass = new DerivedClass();

// access properties and methods of the derived class
Console.WriteLine($"\n{derivedClass.Property1}");
Console.WriteLine($"{derivedClass.Property2}");
derivedClass.Method1();
derivedClass.Method2();

// access properties and methods of the base class that references the derived class
Console.WriteLine($"\n{baseClassReferencingDerivedClass.Property1}");
Console.WriteLine($"{baseClassReferencingDerivedClass.Property2}");
baseClassReferencingDerivedClass.Method1();
baseClassReferencingDerivedClass.Method2();

/*Output:
Derived - Property1
Derived - Property2
Derived - Method1
Derived - Method2

Derived - Property1
Base - Property2
Derived - Method1
Base - Method2
*/

public abstract class BaseClass
{
    public abstract string Property1 { get; set; }
    public virtual string Property2 { get; set; } = "Base - Property2";

    public abstract void Method1();

    public void Method2()
    {
        Console.WriteLine("Base - Method2");
    }
}

public class DerivedClass : BaseClass
{
    public override string Property1 { get; set; } = "Derived - Property1";
    public new string Property2 { get; set; } = "Derived - Property2";

    public override void Method1()
    {
        Console.WriteLine("Derived - Method1");
    }

    public new void Method2()
    {
        Console.WriteLine("Derived - Method2");
    }
}

Tenga en cuenta que cuando la clase base se declara mediante la palabra clave abstract, la clase base no se puede crear una instancia directamente. En su lugar, debe crear instancias de las clases derivadas que implementan los miembros abstractos. La clase derivada debe proporcionar implementaciones para los miembros abstractos definidos en la clase base. Los miembros que no son abstractos se pueden invalidar o ocultar en la clase derivada.

Invalidar los miembros heredados implícitamente de la clase Object

Todas las clases de C# heredan implícitamente de la clase Object. La clase Object define varios miembros heredados por todas las clases, como ToString, Equalsy GetHashCode. Puede invalidar estos miembros en las clases para proporcionar implementaciones personalizadas.

Tenga en cuenta el ejemplo siguiente:


// create instances of the derived classes
DerivedClass1 derivedClass1 = new DerivedClass1();
DerivedClass2 derivedClass2a = new DerivedClass2 { Property1 = "Value1", Property2 = "Value2" };
DerivedClass2 derivedClass2b = new DerivedClass2 { Property1 = "Value1", Property2 = "Value2" };
DerivedClass2 derivedClass2c = new DerivedClass2 { Property1 = "Value3", Property2 = "Value4" };

// demonstrate Object class methods for DerivedClass1
Console.WriteLine("\nDemonstrating Object class methods for DerivedClass1:");
Console.WriteLine($"ToString: {derivedClass1.ToString()}");
Console.WriteLine($"Equals: {derivedClass1.Equals(new DerivedClass1())}");
Console.WriteLine($"GetHashCode: {derivedClass1.GetHashCode()}");

// demonstrate overridden Object class methods for DerivedClass2
Console.WriteLine("\nDemonstrating overridden Object class methods for DerivedClass2:");
Console.WriteLine($"ToString: {derivedClass2a.ToString()}");
Console.WriteLine($"Equals (derivedClass2a vs derivedClass2b): {derivedClass2a.Equals(derivedClass2b)}"); // should return true
Console.WriteLine($"Equals (derivedClass2a vs derivedClass2c): {derivedClass2a.Equals(derivedClass2c)}"); // should return false
Console.WriteLine($"GetHashCode (derivedClass2a): {derivedClass2a.GetHashCode()}");
Console.WriteLine($"GetHashCode (derivedClass2b): {derivedClass2b.GetHashCode()}");
Console.WriteLine($"GetHashCode (derivedClass2c): {derivedClass2c.GetHashCode()}");

/*Output:
Demonstrating Object class methods for DerivedClass1:
ToString: DerivedClass1
Equals: False
GetHashCode: [HashCode]

Demonstrating overridden Object class methods for DerivedClass2:
ToString: DerivedClass2: Property1 = Value1, Property2 = Value2
Equals (derivedClass2a vs derivedClass2b): True
Equals (derivedClass2a vs derivedClass2c): False
GetHashCode (derivedClass2a): [HashCode]
GetHashCode (derivedClass2b): [HashCode]
GetHashCode (derivedClass2c): [HashCode]
*/

public abstract class BaseClass
{
    public abstract string Property1 { get; set; }
    public virtual string Property2 { get; set; } = "Base - Property2";

    public abstract void Method1();

    public void Method2()
    {
        Console.WriteLine("Base - Method2");
    }
}

public class DerivedClass1 : BaseClass
{
    public override string Property1 { get; set; } = "Derived1 - Property1";
    public new string Property2 { get; set; } = "Derived1 - Property2";

    public override void Method1()
    {
        Console.WriteLine("Derived1 - Method1");
    }

    public new void Method2()
    {
        Console.WriteLine("Derived1 - Method2");
    }
}

public class DerivedClass2 : BaseClass
{
    public override string Property1 { get; set; } = "Derived2 - Property1";
    public new string Property2 { get; set; } = "Derived2 - Property2";

    public override void Method1()
    {
        Console.WriteLine("Derived2 - Method1");
    }

    public new void Method2()
    {
        Console.WriteLine("Derived2 - Method2");
    }

    // Override ToString method
    public override string ToString()
    {
        return $"DerivedClass2: Property1 = {Property1}, Property2 = {Property2}";
    }

    // Override Equals method
    public override bool Equals(object obj)
    {
        if (obj is DerivedClass2 other)
        {
            return Property1 == other.Property1 && Property2 == other.Property2;
        }
        return false;
    }

    // Override GetHashCode method
    public override int GetHashCode()
    {
        return HashCode.Combine(Property1, Property2);
    }
}

En este ejemplo, las clases DerivedClass1 y DerivedClass2 heredan de la clase BaseClass. La clase DerivedClass2 invalida los métodos ToString, Equalsy GetHashCode heredados de la clase Object. El método ToString devuelve una representación de cadena del objeto, el método Equals compara dos objetos para la igualdad y el método GetHashCode devuelve un código hash para el objeto. La clase DerivedClass2 proporciona implementaciones personalizadas para estos métodos para mostrar las propiedades del objeto y comparar objetos en función de sus propiedades.

Resumen

La invalidación de propiedades y métodos en una clase derivada permite ampliar o modificar el comportamiento de la clase base. Mediante el uso de la palabra clave override, puede proporcionar implementaciones personalizadas para propiedades y métodos que se heredan de la clase base. Esto le permite reutilizar el código definido en la clase base y ampliar o modificar el comportamiento de la clase base en la clase derivada. También puede usar la palabra clave new para ocultar propiedades y métodos en la clase base y proporcionar nuevas implementaciones en la clase derivada.