Configuración de clases base y derivadas

Completado

Los términos clase base y clase derivada se usan para describir la relación entre las clases en una jerarquía de herencia. Se usa una clase base para definir un conjunto común de atributos y comportamientos que heredan otras clases. Una clase derivada se usa para definir un conjunto especializado de atributos y comportamientos que extienden o modifican la clase base.

Examen de la naturaleza transitiva de la herencia

En C#, una clase solo puede heredar de una clase base. Esta restricción se conoce como herencia única. La herencia única permite que una clase derivada acceda a las propiedades y métodos de una sola clase base, lo que promueve una jerarquía clara y sencilla. El concepto de herencia múltiple no se admite en C#. Esta restricción evita las complejidades y ambigüedades que pueden surgir al heredar de varias clases base. En su lugar, C# usa interfaces para lograr una funcionalidad similar.

Aunque una clase solo puede heredar de una clase base, la herencia es transitiva. Esto significa que si una clase C hereda de la clase B y la clase B hereda de la clase A, la clase C hereda los miembros declarados en la clase B y en la clase A. Esta propiedad permite jerarquías más profundas y reutilización de código adicional.

Por ejemplo, considere un sistema de administración de vehículos donde tiene una clase base Vehicle, una clase derivada Car que hereda de Vehicley otra clase derivada ElectricCar que hereda de Car. En esta jerarquía, ElectricCar hereda miembros de Car y Vehicle, lo que muestra la naturaleza transitiva de la herencia.

public class Vehicle
{
    public string Make { get; set; }
    public string Model { get; set; }

    public void StartEngine()
    {
        Console.WriteLine("Engine started.");
    }

    public void StopEngine()
    {
        Console.WriteLine("Engine stopped.");
    }
}

public class Car : Vehicle
{
    public int NumberOfDoors { get; set; }

    public void OpenTrunk()
    {
        Console.WriteLine("Trunk opened.");
    }

    public void HonkHorn()
    {
        Console.WriteLine("Horn honked.");
    }

    public void LockDoors()
    {
        Console.WriteLine("Doors locked.");
    }
}

public class ElectricCar : Car
{
    public int BatteryCapacity { get; set; }

    public void ChargeBattery()
    {
        Console.WriteLine("Battery charging.");
    }

    public void DisplayBatteryStatus()
    {
        Console.WriteLine("Battery status displayed.");
    }
}

public class CombustionEngineCar : Car
{
    public int FuelCapacity { get; set; }

    public void Refuel()
    {
        Console.WriteLine("Car refueled.");
    }

    public void CheckOilLevel()
    {
        Console.WriteLine("Oil level checked.");
    }
}

En este ejemplo se muestra un sistema sencillo de gestión de vehículos con las siguientes clases:

  • Vehicle clase: la clase base Vehicle contiene propiedades y métodos comunes para todos los vehículos, como Make, Model, StartEngine y StopEngine.

  • Car clase: la clase Car hereda de Vehicle y agrega propiedades y métodos específicos para automóviles, como NumberOfDoors, OpenTrunk, HonkHorny LockDoors.

  • ElectricCar clase: la clase ElectricCar hereda de Car y agrega propiedades y métodos específicos de automóviles eléctricos, como BatteryCapacity, ChargeBatteryy DisplayBatteryStatus.

  • CombustionEngineCar clase: la clase CombustionEngineCar también hereda de Car y agrega propiedades y métodos específicos de automóviles motores de combustión, como FuelCapacity, Refuely CheckOilLevel.

En este ejemplo, la clase Car hereda miembros de la clase Vehicle y los ElectricCar y CombustionEngineCar heredan miembros de la clase Car. Esta jerarquía muestra la naturaleza transitiva de la herencia, donde ElectricCar y CombustionEngineCar heredan miembros de Car y Vehicle.

Examen de la herencia y visibilidad de los miembros

Cuando una clase hereda de una clase base, se aplican las reglas siguientes:

  • Los constructores estáticos e de instancia no se heredan.
  • Todos los demás miembros de la clase base se heredan, pero los modificadores de acceso afectan a su visibilidad en la clase derivada. Los modificadores de acceso incluyen: public, protected, internaly private.

Miembros públicos

Los miembros públicos son accesibles desde cualquier código que tenga acceso a la clase . Las clases derivadas heredan miembros públicos y son accesibles desde fuera de la jerarquía de clases.


public class BaseClass
{
    public int publicField;
    public void PublicMethod() { }
}

public class DerivedClass : BaseClass
{
    public void AccessPublicMember()
    {
        publicField = 10;
        PublicMethod();
    }
}

En este ejemplo, el DerivedClass hereda los miembros publicField y PublicMethod del BaseClass. El método AccessPublicMember del DerivedClass puede acceder a estos miembros. Los miembros públicos también son accesibles desde código que está fuera de la jerarquía de clases.

Miembros protegidos

Los miembros protegidos son accesibles dentro de la clase en la que se declaran y dentro de las clases derivadas. No son accesibles desde fuera de la jerarquía de clases.


public class BaseClass
{
    protected int protectedField;
    protected void ProtectedMethod() { }
}

public class DerivedClass : BaseClass

{
    public void AccessProtectedMember()
    {
        protectedField = 10;
        ProtectedMethod();
    }
}

En este ejemplo, el DerivedClass hereda los miembros protectedField y ProtectedMethod del BaseClass. El método AccessProtectedMember del DerivedClass puede acceder a estos miembros. Sin embargo, si intenta acceder a miembros protegidos desde fuera de la jerarquía de clases, se genera un error en tiempo de compilación.

Miembros internos

Los miembros internos son accesibles dentro del mismo ensamblado. No son accesibles desde fuera del ensamblado, incluso si se hereda la clase .


public class BaseClass
{
    internal int internalField;
    internal void InternalMethod() { }
}

public class DerivedClass : BaseClass
{
    public void AccessInternalMember()
    {
        internalField = 10;
        InternalMethod();
    }
}

En este ejemplo, el DerivedClass hereda los miembros internalField y InternalMethod del BaseClass. El método AccessInternalMember del DerivedClass puede acceder a estos miembros porque están en el mismo ensamblado. Sin embargo, si intenta acceder a miembros internos desde fuera del ensamblado, se genera un error en tiempo de compilación.

Miembros privados

Solo se puede acceder a los miembros privados dentro de la clase en la que se declaran. Las clases derivadas no heredan miembros privados, por lo que no son accesibles directamente en la clase derivada.


public class BaseClass
{
    private int privateField;
    private void PrivateMethod() { }
}

public class DerivedClass : BaseClass
{
    public void AccessPrivateMember()
    {
        // Can't access privateField or PrivateMethod
    }
}

En este ejemplo, el DerivedClass hereda del BaseClass, pero no puede acceder a los miembros de privateField o PrivateMethod porque son privados.

Examen del uso de palabras clave abstractas, virtuales y selladas en la clase base

Las clases base y derivadas usan las palabras clave abstract, virtualy sealed para controlar el comportamiento de los miembros que se heredan. Estas palabras clave permiten definir el nivel de control que las clases derivadas tienen sobre los miembros de la clase base.

Examen del uso de la palabra clave abstract

La palabra clave abstract en C# se usa para definir clases y miembros de clase que están incompletos y deben implementarse en clases derivadas. Una clase abstracta no se puede crear una instancia directamente y está pensada para ser una clase base para otras clases. Los métodos y propiedades abstractos se declaran sin ninguna implementación y se deben invalidar en clases derivadas que no sean de aabstract.

Por ejemplo:

public abstract class Shape
{
    public abstract int GetArea();
}

public class Square : Shape
{
    private int _side;

    public Square(int side)
    {
        _side = side;
    }

    public override int GetArea()
    {
        return _side * _side;
    }
}

class Program
{
    static void Main()
    {
        Square square = new Square(5);
        Console.WriteLine($"Area of the square = {square.GetArea()}");
    }
}

En este ejemplo, la clase Shape es abstracta y contiene un método abstracto GetArea. La clase Square hereda de Shape y proporciona una implementación para el método GetArea. Se puede crear una instancia de la clase Square y el método GetArea devuelve el área del cuadrado.

Las reglas siguientes describen cómo afecta la palabra clave abstract a la herencia:

  • Clases abstractas: no se puede crear una instancia directa de una clase abstracta. La clase abstracta es una clase base para las clases derivadas y las clases derivadas deben proporcionar implementaciones para todos los miembros abstractos de la clase abstracta.
  • Métodos abstractos: los métodos abstractos se declaran sin ninguna implementación en la clase abstracta. Las clases derivadas deben invalidar estos métodos y proporcionar la implementación.
  • Propiedades abstractas: de forma similar a los métodos abstractos, las propiedades abstractas se declaran sin implementación y se deben invalidar en clases derivadas.

La palabra clave abstract en C# es una herramienta eficaz para definir clases incompletas y miembros que se deben implementar en clases derivadas. Aplica un contrato que las clases derivadas deben seguir, asegurándose de que se implementan determinados métodos y propiedades. El uso adecuado de la palabra clave abstract promueve una delimitación clara de las responsabilidades entre las clases base y derivadas.

Examen del uso de la palabra clave virtual

La palabra clave virtual en C# se usa para definir métodos y propiedades que se pueden invalidar en clases derivadas. Un método virtual o propiedad tiene una implementación en la clase base, pero se puede extender o modificar en clases derivadas. Las clases derivadas pueden invalidar los miembros virtuales para proporcionar sus propias implementaciones.

Por ejemplo:


public class Animal
{
    public virtual void MakeSound()
    {
        Console.WriteLine("Animal makes a sound");
    }
}

public class Dog : Animal
{
    public override void MakeSound()
    {
        Console.WriteLine("Dog barks");
    }
}

class Program
{
    static void Main()
    {
        Animal animal = new Dog();
        animal.MakeSound();
    }
}

En este ejemplo, la clase Animal define un método virtual MakeSound que imprime un mensaje genérico. La clase Dog hereda de Animal e invalida el método MakeSound para imprimir un mensaje específico. Al crear un objeto Dog y llamar al método MakeSound, se ejecuta la implementación invalidada en la clase Dog.

Las reglas siguientes describen cómo afecta la palabra clave virtual a la herencia:

  • Métodos virtuales: un método virtual tiene una implementación en la clase base, pero se puede invalidar en clases derivadas.
  • Propiedades virtuales: de forma similar a los métodos virtuales, las propiedades virtuales tienen una implementación en la clase base y se pueden invalidar en clases derivadas.

Examen del uso de la palabra clave sealed

La palabra clave sealed en C# se usa para evitar que se herede o invalide un miembro de clase o clase. Cuando una clase está marcada como sealed, no se puede usar como clase base para otras clases. Cuando un método se marca como sealed, no se puede invalidar en clases derivadas.

Por ejemplo:


public class BaseClass
{
    public virtual void Method1()
    {
        Console.WriteLine("Method1 in BaseClass");
    }

    public virtual void Method2()
    {
        Console.WriteLine("Method2 in BaseClass");
    }
}

public class DerivedClass : BaseClass
{
    public sealed override void Method1()
    {
        Console.WriteLine("Method1 in DerivedClass");
    }

    public override void Method2()
    {
        Console.WriteLine("Method2 in DerivedClass");
    }
}

public class FinalClass : DerivedClass
{
    // This class can't override Method1 because it's sealed in DerivedClass
    public override void Method2()
    {
        Console.WriteLine("Method2 in FinalClass");
    }
}

En este ejemplo, el DerivedClass hereda del BaseClass e invalida el método Method1, lo marca como sealed. El método Method2 también se invalida en el DerivedClass, pero no está sellado. El FinalClass hereda de DerivedClass e intenta invalidar el método Method2. Sin embargo, no puede invalidar el método Method1 porque está sellado en el DerivedClass.

Las reglas siguientes describen cómo afecta la palabra clave sealed a la herencia:

  • Clases selladas: no se puede usar una clase sellada como clase base para otras clases. Impide la herencia de la clase sellada.
  • Métodos sellados: un método sellado no se puede invalidar en clases derivadas. Evita la modificación adicional del método en clases derivadas.
  • Propiedades selladas: de forma similar a los métodos sellados, las propiedades selladas no se pueden invalidar en clases derivadas.

Las clases y métodos sellados son útiles cuando se desea evitar más extensión o modificación de una clase o método. Proporcionan una manera de restringir la herencia y asegurarse de que determinados miembros permanecen sin cambios.

Examen de la herencia implícita de Object

En C#, todas las clases heredan implícitamente de la clase Object. La clase Object define varios métodos que están disponibles para todas las clases, como ToString, Equalsy GetHashCode. Si una clase no hereda explícitamente de otra clase, todavía hereda de Object de forma predeterminada.

  • ToString: el método ToString devuelve una cadena que representa el objeto actual. De forma predeterminada, devuelve el nombre completo de la clase .
  • Equals: el método Equals compara dos objetos para obtener igualdad. De forma predeterminada, compara las referencias de los objetos .
  • GetHashCode: el método GetHashCode devuelve un código hash para el objeto actual. De forma predeterminada, devuelve el código hash de la referencia del objeto.

Tenga en cuenta el ejemplo de código siguiente que crea tres objetos Person y muestra los métodos heredados ToString, Equalsy GetHashCode:

Person person1 = new Person { Name = "Alice", Age = 30 };
Person person2 = new Person { Name = "Alice", Age = 30 };
Person person3 = person1;

Console.WriteLine(person1.ToString());
Console.WriteLine(person1.Equals(person2));
Console.WriteLine(person1.GetHashCode());
Console.WriteLine(person1.Equals(person3));

public class Person
{
    public string? Name { get; set; }
    public int Age { get; set; }
}

public class Employee : Person
{
    public int EmployeeNumber { get; set; }
    public decimal Salary { get; set; }
}

// Output: Person
//         False
//         32854180
//         True

En este ejemplo de código, el método ToString devuelve el nombre completo de la clase Person, que incluye el espacio de nombres definido. El primer método Equals compara las referencias de los objetos person1 y person2 y devuelve False. El método GetHashCode devuelve el código hash de la referencia del objeto person1. A continuación, el método Equals compara las referencias de los objetos person1 y person3 y devuelve True.

Resumen

La herencia permite a las clases derivadas heredar miembros de clase base que definen un conjunto común de atributos y comportamientos. Las clases derivadas pueden extender o modificar el comportamiento de la clase base agregando nuevos miembros o reemplazando los miembros existentes. Las palabras clave abstract, virtualy sealed se usan para controlar cómo se heredan o invalidan los miembros de clase base. La visibilidad de los miembros heredados se ve afectada por modificadores de acceso, como public, protected, internaly private. Todas las clases de C# heredan implícitamente de la clase Object, que proporciona varios métodos, como ToString, Equalsy GetHashCode, que están disponibles para todas las clases.