Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La herencia, junto con la encapsulación y el polimorfismo, es una de las tres características principales de la programación orientada a objetos. La herencia permite crear nuevas clases que reutilizan, amplían y modifican el comportamiento definido en otras clases. La clase cuyos miembros se heredan se denomina clase base y la clase que hereda esos miembros se denomina clase derivada. Una clase derivada solo puede tener una clase base directa. Sin embargo, la herencia es transitiva. Si ClassC
se deriva de ClassB
y ClassB
se deriva de ClassA
, ClassC
hereda los miembros declarados en ClassB
y ClassA
.
Nota:
Las estructuras no admiten la herencia, pero pueden implementar interfaces.
Conceptualmente, una clase derivada es una especialización de la clase base. Por ejemplo, si tiene una clase Animal
base , es posible que tenga una clase derivada denominada Mammal
y otra clase derivada denominada Reptile
. Un Mammal
es un Animal
, y un Reptile
es un Animal
, pero cada clase derivada representa especializaciones diferentes de la clase base.
Las declaraciones de interfaz pueden definir una implementación predeterminada para sus miembros. Estas implementaciones se heredan mediante interfaces derivadas y por clases que implementan esas interfaces. Para obtener más información sobre los métodos de interfaz predeterminados, consulte el artículo sobre interfaces.
Cuando se define una clase para derivar de otra clase, la clase derivada obtiene implícitamente todos los miembros de la clase base, excepto los constructores y finalizadores. La clase derivada reutiliza el código de la clase base sin tener que volver a implementarlo. Puede agregar más miembros en la clase derivada. La clase derivada amplía 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. Al igual que todas las clases, deriva de System.Object y hereda todos sus métodos.
WorkItem
agrega seis miembros propios. Estos miembros incluyen un constructor, porque los constructores no se heredan. La clase 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 de Object. Debe añadir su propio constructor, además de incluir originalItemID
. La propiedad originalItemID
permite asociar la ChangeRequest
instancia con el original WorkItem
al que se aplica la solicitud de cambio.
En el ejemplo siguiente se muestra cómo se expresan en C#las relaciones de clase que se muestran en la ilustración anterior. En el ejemplo también se muestra cómo WorkItem
invalida el método Object.ToStringvirtual y cómo ChangeRequest
la clase hereda la WorkItem
implementación del método . El primer bloque define las clases:
// WorkItem implicitly inherits from the Object class.
public class WorkItem
{
// Static field currentID stores the job ID of the last WorkItem that
// has been created.
private static int currentID;
//Properties.
protected int ID { get; set; }
protected string Title { get; set; }
protected string Description { get; set; }
protected TimeSpan jobLength { get; set; }
// Default constructor. If a derived class does not invoke a base-
// class constructor explicitly, the default constructor is called
// implicitly.
public WorkItem()
{
ID = 0;
Title = "Default title";
Description = "Default description.";
jobLength = new TimeSpan();
}
// Instance constructor that has three parameters.
public WorkItem(string title, string desc, TimeSpan joblen)
{
this.ID = GetNextID();
this.Title = title;
this.Description = desc;
this.jobLength = joblen;
}
// Static constructor to initialize the static member, currentID. This
// constructor is called one time, automatically, before any instance
// of WorkItem or ChangeRequest is created, or currentID is referenced.
static WorkItem() => currentID = 0;
// currentID is a static field. It is incremented each time a new
// instance of WorkItem is created.
protected int GetNextID() => ++currentID;
// Method Update enables you to update the title and job length of an
// existing WorkItem object.
public void Update(string title, TimeSpan joblen)
{
this.Title = title;
this.jobLength = joblen;
}
// Virtual method override of the ToString method that is inherited
// from System.Object.
public override string ToString() =>
$"{this.ID} - {this.Title}";
}
// ChangeRequest derives from WorkItem and adds a property (originalItemID)
// and two constructors.
public class ChangeRequest : WorkItem
{
protected int originalItemID { get; set; }
// Constructors. Because neither constructor calls a base-class
// constructor explicitly, the default constructor in the base class
// is called implicitly. The base class must contain a default
// constructor.
// Default constructor for the derived class.
public ChangeRequest() { }
// Instance constructor that has four parameters.
public ChangeRequest(string title, string desc, TimeSpan jobLen,
int originalID)
{
// The following properties and the GetNexID method are inherited
// from WorkItem.
this.ID = GetNextID();
this.Title = title;
this.Description = desc;
this.jobLength = jobLen;
// Property originalItemID is a member of ChangeRequest, but not
// of WorkItem.
this.originalItemID = originalID;
}
}
En este siguiente bloque se muestra cómo usar las clases base y derivadas:
// Create an instance of WorkItem by using the constructor in the
// base class that takes three arguments.
WorkItem item = new WorkItem("Fix Bugs",
"Fix all bugs in my code branch",
new TimeSpan(3, 4, 0, 0));
// Create an instance of ChangeRequest by using the constructor in
// the derived class that takes four arguments.
ChangeRequest change = new ChangeRequest("Change Base Class Design",
"Add members to the class",
new TimeSpan(4, 0, 0),
1);
// Use the ToString method defined in WorkItem.
Console.WriteLine(item.ToString());
// Use the inherited Update method to change the title of the
// ChangeRequest object.
change.Update("Change the Design of the Base Class",
new TimeSpan(4, 0, 0));
// ChangeRequest inherits WorkItem's override of ToString.
Console.WriteLine(change.ToString());
/* Output:
1 - Fix Bugs
2 - Change the Design of the Base Class
*/
Métodos abstractos y virtuales
Cuando una clase base declara un método como virtual
, una clase derivada puede override
el método con su propia implementación. Si una clase base declara un miembro como abstract
, ese método debe invalidarse en cualquier clase no abstracta que herede directamente de esa clase. Si una clase derivada es abstracta, hereda miembros abstractos sin implementarlos. Los miembros abstractos y virtuales son la base del polimorfismo, que es la segunda característica principal de la programación orientada a objetos. Para obtener más información, vea Polimorfismo.
Clases base abstractas
Puede declarar una clase como abstracta si desea evitar la creación de instancias directas mediante el nuevo operador. Una clase abstracta solo se puede usar si se deriva de ella una nueva clase. Una clase abstracta puede contener una o más firmas de método que se declaran 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 debe declararse como abstracta. Las clases derivadas que no son abstractas deben proporcionar la implementación de los métodos abstractos de una clase base abstracta.
Interfaces
Una interfaz es un tipo de referencia que define un conjunto de miembros. Todas las clases y estructuras que implementan esa interfaz deben implementar ese conjunto de miembros. Una interfaz puede definir una implementación predeterminada para cualquiera o todos estos miembros. Una clase puede implementar varias interfaces aunque solo pueda derivar de una sola clase base directa.
Las interfaces se usan para definir funciones específicas para clases que no tienen necesariamente una relación "es un/una". Por ejemplo, cualquier clase o estructura puede implementar la System.IEquatable<T> interfaz para determinar si dos objetos del tipo son equivalentes (sin embargo, el tipo define la equivalencia).
IEquatable<T> no implica el mismo tipo de relación "es una" que existe entre una clase base y una clase derivada (por ejemplo, un Mammal
es un Animal
). Para obtener más información, consulte Interfaces.
Prevención de derivación adicional
Una clase puede impedir que otras clases hereden de ella, o de cualquiera de sus miembros, declarando a sí mismo o al miembro como sealed
.
Clase derivada que oculta miembros de clase base
Una clase derivada puede ocultar los miembros de clase base declarando miembros con el mismo nombre y firma. Se puede usar el modificador new
para indicar de forma explícita que el miembro no está diseñado para reemplazar al miembro base. El uso de new
no es necesario, pero se generará una advertencia del compilador si new
no se usa. Para obtener más información, vea Control de versiones con las palabras clave Override y New y Saber cuándo usar las palabras clave Override y New.