Herencia (Guía de programación de C#)

Actualización: Julio de 2008

La herencia, junto con la encapsulación y el polimorfismo, es una de las tres características principales (o "pilares") de la programación orientada a objetos. La herencia permite crear nuevas clases que reutilizan, extienden y modifican el comportamiento que se define en otras clases. La clase cuyos miembros se heredan se denomina clase base y la clase que hereda esos miembros se denomina clase derivada.

Nota:

Las estructuras no admiten la herencia pero pueden implementar interfaces. Para obtener más información, vea Interfaces (Guía de programación de C#).

Conceptualmente, una clase derivada es una especialización de la clase base. Por ejemplo, si tiene una clase base Animal, puede tener una clase derivada denominada Mammal y otra clase derivada denominada Reptile. Mammal es Animaly Reptile es Animal, pero cada clase derivada representa especializaciones diferentes de la clase base.

Al definir una clase para derivar de otra clase, la clase derivada obtiene implícitamente todos los miembros de la clase base, salvo sus constructores y destructores. La clase derivada puede, por tanto, reutilizar el código de la clase base sin tener que volver a implementarlo. En la clase derivada, puede agregar más miembros. De esta manera, la clase derivada extiende la funcionalidad de la clase base.

En la ilustración siguiente se muestra una clase WorkItem que representa un elemento de trabajo en algún proceso de negocio. Como todas las clases, deriva de System.Object y hereda todos sus métodos. WorkItem agrega cinco miembros propios. Esto incluye un constructor, porque los constructores no se heredan. ChangeRequest hereda de WorkItem y representa un tipo determinado de elemento de trabajo. ChangeRequest agrega dos miembros más a los miembros que hereda de WorkItem y Object. Debe agregar su propio constructor y también agrega un miembro que permitirá asociar ChangeRequest al elemento WorkItem original al que se aplica el cambio.

Herencia de clases

Herencia de clases

En el ejemplo siguiente se muestra cómo se expresan en C# las relaciones de clase presentadas en la ilustración anterior. En el ejemplo también se muestra cómo WorkItem invalida el método virtual Object.ToString y cómo la clase ChangeRequest hereda la implementación de WorkItem del método.

// WorkItem implicitly inherits from Object class
public class WorkItem
{
    private static int nextID;
    protected int ID { get; set; }
    protected TimeSpan jobLength { get; set; }
    protected string Title { get; set; }
    protected string Description { get; set; }
    // Default constructor
    public WorkItem() 
    {
        ID = 0;
        Title = "Default title";
        Description = "Default description.";
        jobLength = new TimeSpan();
    }
    // Static constructor for static member.
    static WorkItem()
    {
        nextID = 0;
    }
    // Instance constructor.
    public WorkItem( string title, string desc, TimeSpan joblen)
    {
        this.ID = GetNextID();                
        this.Title = title;
        this.Description = desc;
        this.jobLength = joblen;
    }
    protected int GetNextID()
    {
       return ++nextID;
    }
    public void Update(string title, TimeSpan joblen)
    {
        this.Title = title;
        this.jobLength = joblen;
    }

    // Virtual method override.
    public override string ToString()
    {
        return String.Format("{0} - {1}", this.ID, this.Title); 
    }
}

// ChangeRequest derives from WorkItem and adds two of its own members.
public class ChangeRequest : WorkItem
{
    protected int originalItemID {get; set;}
    public ChangeRequest() { }
    public ChangeRequest(string title, string desc, TimeSpan jobLen, int originalID)
    {
        this.ID = GetNextID();
        this.Title = title;
        this.Description = desc;
        this.jobLength = jobLen;
        this.originalItemID = originalID;
    }
}

class Program
{
    static void Main()
    {
        WorkItem item = new WorkItem(                                
                        "Fix Bugs", 
                        "Fix all bugs in my source code branch",
                        new TimeSpan(3, 4, 0, 0));

        ChangeRequest change = new ChangeRequest("Change design of base class",
                                                 "Add members to base class",
                                                 new TimeSpan(4, 0, 0),
                                                 1);

        Console.WriteLine(item.ToString());

        // ChangeRequest inherits WorkItem's override of ToString
        Console.WriteLine(change.ToString()); 

        // Keep the console open in debug mode.
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }
}
/* Output:
    1 - Fix Bugs
    2 - Change design of base class
*/

Métodos abstractos y virtuales

Cuando una clase base declara un método como virtual, una clase derivada puede invalidar el método con su propia implementación. Si una clase base declara un miembro como abstracto, ese método se debe invalidar en cualquier clase no abstracta que herede directamente de dicha clase. Si una clase derivada es abstracta en sí misma, hereda los miembros abstractos sin implementarlos. Los miembros abstractos y virtuales son la base para el polimorfismo, la segunda característica principal de la programación orientada a objetos. Para obtener más información, vea Polimorfismo (Guía de programación de C#).

Clases base abstractas

Puede declarar una clase como abstracta si desea evitar la creación directa de instancias por medio de la palabra clave new. Si hace esto, la clase solo se puede utilizar si una nueva clase se deriva de ella. Una clase abstracta puede contener una o más firmas de método que se declaran a sí mismas como abstractas. Estas firmas especifican los parámetros y el valor devuelto pero no tienen ninguna implementación (cuerpo del método). Una clase abstracta no tiene que contener miembros abstractos; sin embargo, si una clase contiene un miembro abstracto, la propia clase se debe declarar como abstracta. Las clases derivadas que no son abstractas por sí mismas deben proporcionar la implementación de cualquier método abstracto de una clase base abstracta. Para obtener más información, vea Clases y miembros de clase abstractos y sellados (Guía de programación de C#) y Diseño de clases abstractas.

Interfaces

Una interfaz es un tipo de referencia similar en cierto modo a una clase base abstracta compuesta únicamente por miembros abstractos. Cuando una clase deriva de una interfaz, debe proporcionar una implementación para todos los miembros de la interfaz. Una clase puede implementar varias interfaces aunque solo puede derivar de una única clase base directa.

Las interfaces se utilizan para definir funciones específicas para las clases que no tienen necesariamente una relación de identidad. Por ejemplo, cualquier clase o estructura que tenga que habilitar el código de cliente para determinar si dos objetos de un tipo son equivalentes (cualquiera que sea la forma en que el tipo defina la equivalencia), puede implementar la interfaz IEquatable[`1]. IEquatable<T> no implica el mismo tipo de relación de identidad que existe entre una clase base y una clase derivada (por ejemplo, Mammal es Animal). Para obtener más información, vea Interfaces (Guía de programación de C#).

Acceso de la clase derivada a los miembros de la clase base

Una clase derivada tiene acceso a los miembros públicos, protegidos, internos e internos protegidos de una clase base. Aunque una clase derivada hereda los miembros privados de una clase base, no puede tener acceso a estos miembros. Sin embargo, todos los miembros privados siguen presentes en la clase derivada y pueden hacer el mismo trabajo que harían en la propia clase base. Por ejemplo, supongamos que un método protegido de la clase base tiene acceso a un campo privado. Este campo debe estar presente en la clase derivada para que el método heredado de la clase base funcione correctamente.

Evitar la derivación adicional

Una clase puede evitar que otras clases hereden de ella, o de cualquiera de sus miembros, declarándose a sí misma o al miembro como sealed. Para obtener más información, vea Clases y miembros de clase abstractos y sellados (Guía de programación de C#).

Ocultar miembros de la clase base en la clase derivada

Una clase derivada puede ocultar miembros de la clase base si los declara con el mismo nombre y firma. Se puede utilizar el modificador new para indicar explícitamente que no se pretende que el miembro sea una invalidación del miembro base. No es necesario utilizar new, pero se generará una advertencia del compilador si no se usa new. Para obtener más información, vea Control de versiones con las palabras clave Override y New (Guía de programación de C#) y Saber cuándo utilizar las palabras clave Override y New (Guía de programación de C#).

Vea también

Conceptos

Guía de programación de C#

Referencia

Clases y estructuras (Guía de programación de C#)

class (Referencia de C#)

struct (Referencia de C#)

Historial de cambios

Fecha

Historial

Motivo

Julio de 2008

Se ha agregado contenido, una ilustración y nuevos ejemplos.

Mejora de la información.