Compartilhar via


Herança – derivar tipos para criar um comportamento mais especializado

A herança, juntamente com o encapsulamento e o polimorfismo, é uma das três características principais da programação orientada a objetos. A herança permite que você crie novas classes que reutilizem, estendam e modifiquem o comportamento definido em outras classes. A classe cujos membros são herdados é chamada de classe base e a classe que herda esses membros é chamada de classe derivada. Uma classe derivada pode ter apenas uma classe base direta. No entanto, a herança é transitiva. Se ClassC for derivado de ClassB, e ClassB for derivado de ClassA, ClassC herdará os membros declarados em ClassB e ClassA.

Observação

Os structs não dão suporte à herança, mas podem implementar interfaces.

Conceitualmente, uma classe derivada é uma especialização da classe base. Por exemplo, se você tiver uma classe Animalbase, poderá ter uma classe derivada nomeada Mammal e outra classe derivada nomeada Reptile. A Mammal é um Animal, e um Reptile é um Animal, mas cada classe derivada representa diferentes especializações da classe base.

As declarações de interface podem definir uma implementação padrão para seus membros. Essas implementações são herdadas por interfaces derivadas e por classes que implementam essas interfaces. Para obter mais informações sobre métodos de interface padrão, consulte o artigo sobre interfaces.

Quando você define uma classe para derivar de outra classe, a classe derivada obtém implicitamente todos os membros da classe base, exceto seus construtores e finalizadores. A classe derivada reutiliza o código na classe base sem precisar reimplementá-lo. Você pode adicionar mais membros na classe derivada. A classe derivada estende a funcionalidade da classe base.

A ilustração a seguir mostra uma classe WorkItem que representa um item de trabalho em algum processo de negócios. Como todas as classes, ela deriva de System.Object e herda todos os seus métodos. WorkItem adiciona seis membros próprios. Esses membros incluem um construtor, pois os construtores não são herdados. A classe ChangeRequest herda WorkItem e representa um tipo específico de item de trabalho. ChangeRequest adiciona mais dois membros aos que herda de WorkItem e de Object. Ele deve adicionar seu próprio construtor e também adiciona originalItemID. A propriedade originalItemID permite que a ChangeRequest instância seja associada ao original WorkItem ao qual a solicitação de alteração se aplica.

Diagrama que mostra a herança de classe

O exemplo a seguir mostra como as relações de classe demonstradas na ilustração anterior são expressas em C#. O exemplo também mostra como WorkItem substitui o método Object.ToStringvirtual e como a ChangeRequest classe herda a WorkItem implementação do método. O primeiro bloco define as classes:

// 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;
    }
}

Este próximo bloco mostra como usar as classes base e 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 abstratos e virtuais

Quando uma classe base declara um método como virtual, uma classe derivada pode override o método com sua própria implementação. Se uma classe base declarar um membro como abstract, esse método deverá ser substituído em qualquer classe não abstrata que herda diretamente dessa classe. Se uma classe derivada for abstrata, ela herdará membros abstratos sem implementá-los. Membros abstratos e virtuais são a base para o polimorfismo, que é a segunda característica primária da programação orientada a objetos. Para obter mais informações, consulte Polymorphism.

Classes base abstratas

Você pode declarar uma classe como abstrata se quiser impedir a instanciação direta usando o novo operador. Uma classe abstrata só poderá ser usada se uma nova classe for derivada dela. Uma classe abstrata pode conter uma ou mais assinaturas de método que são declaradas como abstratas. Essas assinaturas especificam os parâmetros e o valor retornado, mas não têm nenhuma implementação (corpo do método). Uma classe abstrata não precisa conter membros abstratos; no entanto, se uma classe contiver um membro abstrato, a própria classe deverá ser declarada como abstrata. Classes derivadas que não são abstratas devem fornecer a implementação para quaisquer métodos abstratos de uma classe base abstrata.

Interfaces

Uma interface é um tipo de referência que define um conjunto de membros. Todas as classes e structs que implementam essa interface devem implementar esse conjunto de membros. Uma interface pode definir uma implementação padrão para qualquer um ou todos esses membros. Uma classe pode implementar várias interfaces, embora possa derivar apenas de uma única classe base direta.

Interfaces são usadas para definir recursos específicos para classes que não têm necessariamente uma relação do tipo “é um”. Por exemplo, a System.IEquatable<T> interface pode ser implementada por qualquer classe ou struct para determinar se dois objetos do tipo são equivalentes (no entanto, o tipo define equivalência). IEquatable<T> não implica o mesmo tipo de relação "é um" que existe entre uma classe base e uma classe derivada (por exemplo, um Mammal é um Animal). Para obter mais informações, consulte Interfaces.

Impedindo a derivação adicional

Uma classe pode impedir que outras classes herdem dela, ou de qualquer um de seus membros, declarando-se ou o membro como sealed.

Ocultação de membros da classe base pela classe derivada

Uma classe derivada pode ocultar membros da classe base declarando membros com o mesmo nome e assinatura. O modificador new pode ser usado para indicar explicitamente que o membro não pretende ser uma substituição do membro base. O uso de new não é necessário, mas um aviso do compilador será gerado se new não for usado. Para obter mais informações, consulte Versioning com as palavras-chave Override e New e Conhecendo Quando Usar as Palavras-Chave Override e New.