Herança em C# e .NETInheritance in C# and .NET

Este tutorial apresenta a herança em C#.This tutorial introduces you to inheritance in C#. Herança é um recurso das linguagens de programação orientadas a objeto que permite a definição de uma classe base que, por sua vez, fornece uma funcionalidade específica (dados e comportamento), e a definição de classes derivadas que herdam ou substituem essa funcionalidade.Inheritance is a feature of object-oriented programming languages that allows you to define a base class that provides specific functionality (data and behavior) and to define derived classes that either inherit or override that functionality.

Pré-requisitosPrerequisites

Este tutorial presume que você já instalou o .NET Core.This tutorial assumes that you've installed .NET Core. Para obter instruções de instalação, confira Guia de instalação do .NET Core.For installation instructions, see .NET Core installation guide. Você também precisa de um editor de código.You also need a code editor. Este tutorial usa o Visual Studio Code, embora você possa usar qualquer editor de código que quiser.This tutorial uses Visual Studio Code, although you can use any code editor of your choice.

Como executar os exemplosRunning the examples

Para criar e executar os exemplos neste tutorial, use o utilitário dotnet na linha de comando.To create and run the examples in this tutorial, you use the dotnet utility from the command line. Execute estas etapas para cada exemplo:Follow these steps for each example:

  1. Crie um diretório para armazenar o exemplo.Create a directory to store the example.
  2. Insira o comando dotnet new console no prompt de comando para criar um novo projeto do .NET Core.Enter the dotnet new console command at a command prompt to create a new .NET Core project.
  3. Copie e cole o código do exemplo em seu editor de código.Copy and paste the code from the example into your code editor.
  4. Insira o comando dotnet restore na linha de comando para carregar ou restaurar as dependências do projeto.Enter the dotnet restore command from the command line to load or restore the project's dependencies.

Observação

Começando com o SDK do .NET Core 2.0, não é necessário executar dotnet restore, pois ele é executado implicitamente por todos os comandos que exigem uma restauração, como dotnet new, dotnet build e dotnet run.Starting with .NET Core 2.0 SDK, you don't have to run dotnet restore because it's run implicitly by all commands that require a restore to occur, such as dotnet new, dotnet build and dotnet run. Ainda é um comando válido em determinados cenários em que realizar uma restauração explícita faz sentido, como builds de integração contínua no Azure DevOps Services ou em sistemas de build que precisam controlar explicitamente o horário em que a restauração ocorrerá.It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as continuous integration builds in Azure DevOps Services or in build systems that need to explicitly control the time at which the restore occurs.

  1. Insira o comando dotnet run para compilar e executar o exemplo.Enter the dotnet run command to compile and execute the example.

Segundo plano: O que é a herança?Background: What is inheritance?

Herança é um dos atributos fundamentais da programação orientada a objeto.Inheritance is one of the fundamental attributes of object-oriented programming. Ela permite que você defina uma classe filha que reutiliza (herda), estende ou modifica o comportamento de uma classe pai.It allows you to define a child class that reuses (inherits), extends, or modifies the behavior of a parent class. A classe cujos membros são herdados é chamada de classe base.The class whose members are inherited is called the base class. A classe que herda os membros da classe base é chamada de classe derivada.The class that inherits the members of the base class is called the derived class.

C# e .NET oferecem suporte apenas à herança única.C# and .NET support single inheritance only. Ou seja, uma classe pode herdar apenas de uma única classe.That is, a class can only inherit from a single class. No entanto, a herança é transitiva, o que permite que você defina uma hierarquia de herança para um conjunto de tipos.However, inheritance is transitive, which allows you to define an inheritance hierarchy for a set of types. Em outras palavras, o tipo D pode herdar do tipo C, que herda do tipo B, que herda do tipo de classe base A.In other words, type D can inherit from type C, which inherits from type B, which inherits from the base class type A. Como a herança é transitiva, os membros do tipo A estão disponíveis ao tipo D.Because inheritance is transitive, the members of type A are available to type D.

Nem todos os membros de uma classe base são herdados por classes derivadas.Not all members of a base class are inherited by derived classes. Os membros a seguir não são herdados:The following members are not inherited:

  • Construtores estáticos, que inicializam os dados estáticos de uma classe.Static constructors, which initialize the static data of a class.

  • Construtores de instância, que você chama para criar uma nova instância da classe.Instance constructors, which you call to create a new instance of the class. Cada classe deve definir seus próprios construtores.Each class must define its own constructors.

  • Finalizadores, que são chamados pelo coletor de lixo do tempo de execução para destruir as instâncias de uma classe.Finalizers, which are called by the runtime's garbage collector to destroy instances of a class.

Enquanto todos os outros membros de uma classe base são herdados por classes derivadas, o fato de serem visíveis ou não depende de sua acessibilidade.While all other members of a base class are inherited by derived classes, whether they are visible or not depends on their accessibility. A acessibilidade de um membro afeta sua visibilidade para classes derivadas da seguinte maneira:A member's accessibility affects its visibility for derived classes as follows:

  • Membros Privados são visíveis apenas em classes derivadas que estão aninhadas em sua classe base.Private members are visible only in derived classes that are nested in their base class. Caso contrário, eles não são visíveis em classes derivadas.Otherwise, they are not visible in derived classes. No exemplo a seguir, A.B é uma classe aninhada derivada de A, e C deriva de A.In the following example, A.B is a nested class that derives from A, and C derives from A. O campo A.value privado fica visível em A.B.The private A.value field is visible in A.B. No entanto, se você remover os comentários do método C.GetValue e tentar compilar o exemplo, ele produzirá um erro do compilador CS0122: "'A.value' está inacessível devido ao seu nível de proteção".However, if you remove the comments from the C.GetValue method and attempt to compile the example, it produces compiler error CS0122: "'A.value' is inaccessible due to its protection level."

    using System;
    
    public class A 
    {
       private int value = 10;
    
       public class B : A
       {
           public int GetValue()
           {
               return this.value;
           }     
       }
    }
    
    public class C : A
    {
    //    public int GetValue()
    //    {
    //        return this.value;
    //    }
    }
    
    public class Example
    {
        public static void Main(string[] args)
        {
            var b = new A.B();
            Console.WriteLine(b.GetValue());
        }
    }
    // The example displays the following output:
    //       10
    
  • Membros Protegidos são visíveis apenas em classes derivadas.Protected members are visible only in derived classes.

  • Membros Internos são visíveis apenas em classes derivadas localizadas no mesmo assembly que a classe base.Internal members are visible only in derived classes that are located in the same assembly as the base class. Eles não são visíveis em classes derivadas localizadas em um assembly diferente da classe base.They are not visible in derived classes located in a different assembly from the base class.

  • Membros Públicos são visíveis em classes derivadas e fazem parte da interface pública da classe derivada.Public members are visible in derived classes and are part of the derived class' public interface. Os membros públicos herdados podem ser chamados como se estivessem definidos na classe derivada.Public inherited members can be called just as if they are defined in the derived class. No exemplo a seguir, a classe A define um método chamado Method1, e a classe B herda da classe A.In the following example, class A defines a method named Method1, and class B inherits from class A. Depois, o exemplo chama Method1 como se fosse um método de instância em B.The example then calls Method1 as if it were an instance method on B.

public class A
{
    public void Method1()
    {
        // Method implementation.
    }
}

public class B : A
{ }


public class Example
{
    public static void Main()
    {
        B b = new B();
        b.Method1();
    }
}

Classes derivadas também podem substituir membros herdados fornecendo uma implementação alternativa.Derived classes can also override inherited members by providing an alternate implementation. Para poder substituir um membro, o membro na classe base deve ser marcado com a palavra-chave virtual.In order to be able to override a member, the member in the base class must be marked with the virtual keyword. Por padrão, os membros da classe base não são marcados como virtual e não podem ser substituídos.By default, base class members are not marked as virtual and cannot be overridden. A tentativa de substituir um membro não virtual, como faz o exemplo a seguir, gera o erro do compilador CS0506: "O <member> não pode substituir o membro herdado <member>, pois não está marcado como virtual, abstrato ou de substituição".Attempting to override a non-virtual member, as the following example does, generates compiler error CS0506: "<member> cannot override inherited member <member> because it is not marked virtual, abstract, or override.

public class A
{
    public void Method1()
    {
        // Do something.
    }
}

public class B : A
{
    public override void Method1() // Generates CS0506.
    {
        // Do something else.
    }
}

Em alguns casos, uma classe derivada deve substituir a implementação da classe base.In some cases, a derived class must override the base class implementation. Membros de classe base marcados com a palavra-chave abstrato exigem que as classes derivadas os substituam.Base class members marked with the abstract keyword require that derived classes override them. A tentativa de compilar o exemplo a seguir gera um erro do compilador CS0534, a "<classe> não implementa o membro abstrato herdado <membro>", pois a classe B não fornece uma implementação para A.Method1.Attempting to compile the following example generates compiler error CS0534, "<class> does not implement inherited abstract member <member>", because class B provides no implementation for A.Method1.

public abstract class A
{
    public abstract void Method1();
}

public class B : A // Generates CS0534.
{
    public void Method3()
    {
        // Do something.
    }
}

A herança se aplica apenas a classes e interfaces.Inheritance applies only to classes and interfaces. Outras categorias de tipo (structs, delegados e enumerações) não dão suporte à herança.Other type categories (structs, delegates, and enums) do not support inheritance. Devido a essas regras, a tentativa de compilar o código como no exemplo a seguir gera o erro do compilador CS0527: “O tipo ‘ValueType’ na lista de interfaces não é uma interface”.Because of these rules, attempting to compile code like the following example produces compiler error CS0527: "Type 'ValueType' in interface list is not an interface." A mensagem de erro indica que, embora você possa definir as interfaces implementadas por um struct, não há suporte para a herança.The error message indicates that, although you can define the interfaces that a struct implements, inheritance is not supported.

using System;

public struct ValueStructure : ValueType // Generates CS0527.
{
}

Herança implícitaImplicit inheritance

Apesar dos tipos possíveis de herança por meio de herança única, todos os tipos no sistema de tipo .NET herdam implicitamente de Object ou de um tipo derivado dele.Besides any types that they may inherit from through single inheritance, all types in the .NET type system implicitly inherit from Object or a type derived from it. A funcionalidade comum de Object está disponível para qualquer tipo.The common functionality of Object is available to any type.

Para ver o que significa herança implícita, vamos definir uma nova classe, SimpleClass, que é simplesmente uma definição de classe vazia:To see what implicit inheritance means, let's define a new class, SimpleClass, that is simply an empty class definition:

public class SimpleClass
{ }

É possível usar reflexão (o que permite inspecionar os metadados de um tipo para obter informações sobre esse tipo) para obter uma lista dos membros que pertencem ao tipo SimpleClass.You can then use reflection (which lets you inspect a type's metadata to get information about that type) to get a list of the members that belong to the SimpleClass type. Embora você ainda não tenha definido membros na classe SimpleClass, a saída do exemplo indica que, na verdade, ela tem nove membros.Although you haven't defined any members in your SimpleClass class, output from the example indicates that it actually has nine members. Um desses membros é um construtor sem parâmetros (ou padrão) que é fornecido automaticamente para o tipo SimpleClass pelo compilador de C#.One of these members is a parameterless (or default) constructor that is automatically supplied for the SimpleClass type by the C# compiler. Os oito são membros do Object, o tipo do qual todas as classes e interfaces no sistema do tipo .NET herdam implicitamente.The remaining eight are members of Object, the type from which all classes and interfaces in the .NET type system ultimately implicitly inherit.

using System;
using System.Reflection;

public class Example
{
    public static void Main()
    {
        Type t = typeof(SimpleClass);
        BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
                             BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
        MemberInfo[] members = t.GetMembers(flags);
        Console.WriteLine($"Type {t.Name} has {members.Length} members: ");
        foreach (var member in members)
        {
            string access = "";
            string stat = "";
            var method = member as MethodBase;
            if (method != null)
            {
                if (method.IsPublic)
                    access = " Public";
                else if (method.IsPrivate)
                    access = " Private";
                else if (method.IsFamily)
                    access = " Protected";
                else if (method.IsAssembly)
                    access = " Internal";
                else if (method.IsFamilyOrAssembly)
                    access = " Protected Internal ";
                if (method.IsStatic)
                    stat = " Static";
            }
            var output = $"{member.Name} ({member.MemberType}): {access}{stat}, Declared by {member.DeclaringType}";
            Console.WriteLine(output);

        }
    }
}
// The example displays the following output:
//	Type SimpleClass has 9 members:
//	ToString (Method):  Public, Declared by System.Object
//	Equals (Method):  Public, Declared by System.Object
//	Equals (Method):  Public Static, Declared by System.Object
//	ReferenceEquals (Method):  Public Static, Declared by System.Object
//	GetHashCode (Method):  Public, Declared by System.Object
//	GetType (Method):  Public, Declared by System.Object
//	Finalize (Method):  Internal, Declared by System.Object
//	MemberwiseClone (Method):  Internal, Declared by System.Object
//	.ctor (Constructor):  Public, Declared by SimpleClass

A herança implícita da classe Object torna esses métodos disponíveis para a classe SimpleClass:Implicit inheritance from the Object class makes these methods available to the SimpleClass class:

  • O método ToString público, que converte um objeto SimpleClass em sua representação de cadeia de caracteres, retorna o nome de tipo totalmente qualificado.The public ToString method, which converts a SimpleClass object to its string representation, returns the fully qualified type name. Nesse caso, o método ToString retorna a cadeia de caracteres "SimpleClass".In this case, the ToString method returns the string "SimpleClass".

  • Três métodos de teste de igualdade de dois objetos: o método Equals(Object) da instância pública, o método Equals(Object, Object) do público estático e o método ReferenceEquals(Object, Object) de público estático.Three methods that test for equality of two objects: the public instance Equals(Object) method, the public static Equals(Object, Object) method, and the public static ReferenceEquals(Object, Object) method. Por padrão, esses métodos testam a igualdade de referência; ou seja, para que seja iguais, duas variáveis de objeto devem fazer referência ao mesmo objeto.By default, these methods test for reference equality; that is, to be equal, two object variables must refer to the same object.

  • O método público GetHashCode, que calcula um valor que permite o uso de uma instância do tipo em conjuntos de hash.The public GetHashCode method, which computes a value that allows an instance of the type to be used in hashed collections.

  • O método público GetType, que retorna um objeto Type que representa o tipo SimpleClass.The public GetType method, which returns a Type object that represents the SimpleClass type.

  • O método protegido Finalize, que é projetado para liberar recursos não gerenciados antes que a memória de um objeto seja reivindicada pelo coletor de lixo.The protected Finalize method, which is designed to release unmanaged resources before an object's memory is reclaimed by the garbage collector.

  • O método protegido MemberwiseClone, que cria um clone superficial do objeto atual.The protected MemberwiseClone method, which creates a shallow clone of the current object.

Devido à herança implícita, você pode chamar qualquer membro herdado de um objeto SimpleClass como se ele fosse, na verdade, um membro definido na classe SimpleClass.Because of implicit inheritance, you can call any inherited member from a SimpleClass object just as if it was actually a member defined in the SimpleClass class. Por exemplo, o exemplo a seguir chama o método SimpleClass.ToString, que SimpleClass herda de Object.For instance, the following example calls the SimpleClass.ToString method, which SimpleClass inherits from Object.

using System;

public class SimpleClass
{}

public class Example
{
    public static void Main()
    {
        SimpleClass sc = new SimpleClass();
        Console.WriteLine(sc.ToString());
    }
}
// The example displays the following output:
//        SimpleClass

A tabela a seguir lista as categorias de tipos que você pode criar em C#, e os tipos de onde eles herdam implicitamente.The following table lists the categories of types that you can create in C# and the types from which they implicitly inherit. Cada tipo base disponibiliza um conjunto diferente de membros por meio de herança para tipos derivados implicitamente.Each base type makes a different set of members available through inheritance to implicitly derived types.

Categoria do tipoType category Herda implicitamente deImplicitly inherits from
classeclass Object
structstruct ValueType, ObjectValueType, Object
enumenum Enum, ValueType, ObjectEnum, ValueType, Object
delegadodelegate MulticastDelegate, Delegate, ObjectMulticastDelegate, Delegate, Object

Herança e um relacionamento "é um(a)"Inheritance and an "is a" relationship

Normalmente, a herança é usada para expressar um relacionamento "é um(a)" entre uma classe base e uma ou mais classes derivadas, em que as classes derivadas são versões especializadas da classe base; a classe derivada é um tipo de classe base.Ordinarily, inheritance is used to express an "is a" relationship between a base class and one or more derived classes, where the derived classes are specialized versions of the base class; the derived class is a type of the base class. Por exemplo, a classe Publication representa uma publicação de qualquer tipo e as classes Book e Magazine representam tipos específicos de publicações.For example, the Publication class represents a publication of any kind, and the Book and Magazine classes represent specific types of publications.

Observação

Uma classe ou struct pode implementar uma ou mais interfaces.A class or struct can implement one or more interfaces. Embora a implementação da interface seja apresentada geralmente como uma alternativa para herança única, ou como uma forma de usar a herança com structs, ela tem como objetivo expressar um relacionamento diferente (um relacionamento "pode fazer") entre uma interface e seu tipo de implementação em comparação com a herança.While interface implementation is often presented as a workaround for single inheritance or as a way of using inheritance with structs, it is intended to express a different relationship (a "can do" relationship) between an interface and its implementing type than inheritance. Uma interface define um subconjunto de funcionalidades (como a capacidade de testar a igualdade, comparar ou classificar objetos ou dar suporte à formatação e análise sensível à cultura) que disponibiliza para seus tipos de implementação.An interface defines a subset of functionality (such as the ability to test for equality, to compare or sort objects, or to support culture-sensitive parsing and formatting) that the interface makes available to its implementing types.

Observe que "é um(a)" também expressa o relacionamento entre um tipo e uma instanciação específica desse tipo.Note that "is a" also expresses the relationship between a type and a specific instantiation of that type. No exemplo a seguir, Automobile é uma classe que tem três propriedades somente leitura exclusivas: Make, o fabricante do automóvel; Model, o tipo de automóvel; e Year, o ano de fabricação.In the following example, Automobile is a class that has three unique read-only properties: Make, the manufacturer of the automobile; Model, the kind of automobile; and Year, its year of manufacture. A classe Automobile também tem um construtor cujos argumentos são atribuídos aos valores de propriedade. Ela também substitui o método Object.ToString para produzir uma cadeia de caracteres que identifica exclusivamente a instância Automobile em vez da classe Automobile.Your Automobile class also has a constructor whose arguments are assigned to the property values, and it overrides the Object.ToString method to produce a string that uniquely identifies the Automobile instance rather than the Automobile class.

using System;

public class Automobile
{
    public Automobile(string make, string model, int year)
    {
        if (make == null)
           throw new ArgumentNullException("The make cannot be null.");
        else if (String.IsNullOrWhiteSpace(make))
           throw new ArgumentException("make cannot be an empty string or have space characters only.");
        Make = make;

        if (model == null)
           throw new ArgumentNullException("The model cannot be null.");
        else if (String.IsNullOrWhiteSpace(model))
           throw new ArgumentException("model cannot be an empty string or have space characters only.");
        Model = model;

        if (year < 1857 || year > DateTime.Now.Year + 2)
           throw new ArgumentException("The year is out of range.");
        Year = year;
    }

    public string Make { get; }
    
    public string Model { get; }

    public int Year { get; }

    public override string ToString() => $"{Year} {Make} {Model}";
}

Nesse caso, você não deve depender da herança para representar marcas e modelos de carro específicos.In this case, you shouldn't rely on inheritance to represent specific car makes and models. Por exemplo, você não precisa definir um tipo Packard para representar automóveis fabricados pela empresa Packard Motor Car.For example, you don't need to define a Packard type to represent automobiles manufactured by the Packard Motor Car Company. Nesse caso, é possível representá-los criando um objeto Automobile com os valores apropriados passados ao construtor de classe, como no exemplo a seguir.Instead, you can represent them by creating an Automobile object with the appropriate values passed to its class constructor, as the following example does.

using System;

public class Example
{
    public static void Main()
    {
        var packard = new Automobile("Packard", "Custom Eight", 1948);
        Console.WriteLine(packard);
    }
}
// The example displays the following output:
//        1948 Packard Custom Eight

Um relacionamento é-um(a) baseado na herança é mais bem aplicado a uma classe base e em classes derivadas que adicionam outros membros à classe base, ou que exigem funcionalidades adicionais não incluídas na classe base.An is-a relationship based on inheritance is best applied to a base class and to derived classes that add additional members to the base class or that require additional functionality not present in the base class.

Criação da classe base e das classes derivadasDesigning the base class and derived classes

Vamos examinar o processo de criação de uma classe base e de suas classes derivadas.Let's look at the process of designing a base class and its derived classes. Nesta seção, você definirá uma classe base, a Publication, que representa uma publicação de qualquer tipo, como um livro, uma revista, um jornal, um diário, um artigo etc. Você também definirá uma classe Book derivada de Publication.In this section, you'll define a base class, Publication, which represents a publication of any kind, such as a book, a magazine, a newspaper, a journal, an article, etc. You'll also define a Book class that derives from Publication. É possível estender facilmente o exemplo para definir outras classes derivadas, como Magazine, Journal, Newspaper e Article.You could easily extend the example to define other derived classes, such as Magazine, Journal, Newspaper, and Article.

A classe base de PublicaçãoThe base Publication class

Em projetar a classe Publication, você precisa tomar várias decisões de design:In designing your Publication class, you need to make several design decisions:

  • Quais membros devem ser incluídos na classe base Publication e se os membros de Publication fornecem implementações de método, ou se Publication é uma classe base abstrata que funciona como um modelo para suas classes derivadas.What members to include in your base Publication class, and whether the Publication members provide method implementations or whether Publication is an abstract base class that serves as a template for its derived classes.

    Nesse caso, a classe Publication fornecerá implementações de método.In this case, the Publication class will provide method implementations. A seção Criação de classes base abstratas e de suas classes derivadas contém um exemplo que usa uma classe base abstrata para definir os métodos que as classes derivadas devem substituir.The Designing abstract base classes and their derived classes section contains an example that uses an abstract base class to define the methods that derived classes must override. As classes derivadas são livres para fornecer qualquer implementação adequada ao tipo derivado.Derived classes are free to provide any implementation that is suitable for the derived type.

    A capacidade de reutilizar o código (ou seja, várias classes derivadas compartilham a declaração e a implementação dos métodos de classe base, e não é necessário substituí-las) é uma vantagem das classes base não abstratas.The ability to reuse code (that is, multiple derived classes share the declaration and implementation of base class methods and do not need to override them) is an advantage of non-abstract base classes. Portanto, você deverá adicionar membros à Publication se o código precisar ser compartilhado por um ou mais tipos Publication especializados.Therefore, you should add members to Publication if their code is likely to be shared by some or most specialized Publication types. Se você não conseguir fornecer implementações da classe base de forma eficiente, será necessário fornecer implementações de membro praticamente idênticas em classes derivadas em vez de uma única implementação na classe base.If you fail to provide base class implementations efficiently, you'll end up having to provide largely identical member implementations in derived classes rather a single implementation in the base class. A necessidade de manter o código duplicado em vários locais é uma possível fonte de bugs.The need to maintain duplicated code in multiple locations is a potential source of bugs.

    Para maximizar a reutilização do código e criar uma hierarquia de herança lógica e intuitiva, inclua na classe Publication apenas dos dados e a funcionalidade comuns a todas as publicações ou à maioria delas.Both to maximize code reuse and to create a logical and intuitive inheritance hierarchy, you want to be sure that you include in the Publication class only the data and functionality that is common to all or to most publications. Depois, as classes derivadas implementam os membros exclusivos a determinados tipos de publicação que eles representam.Derived classes then implement members that are unique to the particular kinds of publication that they represent.

  • O quanto devemos ampliar a hierarquia de classe.How far to extend your class hierarchy. Você deseja desenvolver uma hierarquia de três ou mais classes, em vez de simplesmente uma classe base e uma ou mais classes derivadas?Do you want to develop a hierarchy of three or more classes, rather than simply a base class and one or more derived classes? Por exemplo, Publication poderia ser uma classe base de Periodical, que por sua vez é uma classe base de Magazine, Journal e Newspaper.For example, Publication could be a base class of Periodical, which in turn is a base class of Magazine, Journal and Newspaper.

    Para o seu exemplo, você usará a hierarquia pequena de uma classe Publication e uma única classe derivada, a Book.For your example, you'll use the small hierarchy of a Publication class and a single derived class, Book. É possível ampliar o exemplo facilmente para criar várias classes adicionais derivadas de Publication, como Magazine e Article.You could easily extend the example to create a number of additional classes that derive from Publication, such as Magazine and Article.

  • Se faz sentido instanciar a classe base.Whether it makes sense to instantiate the base class. Se não fizer, você deverá aplicar a palavra-chave abstract à classe.If it does not, you should apply the abstract keyword to the class. Caso contrário, a instância da classe Publication poderá ser criada chamando seu construtor de classe.Otherwise, your Publication class can be instantiated by calling its class constructor. Se for feita uma tentativa de instanciar uma classe marcada com a palavra-chave abstract por uma chamada direta para o construtor de classe, o compilador de C# gerará o erro CS0144, "Não é possível criar uma instância da classe abstrata ou interface".If an attempt is made to instantiate a class marked with the abstract keyword by a direct call to its class constructor, the C# compiler generates error CS0144, "Cannot create an instance of the abstract class or interface." Se for feita uma tentativa de instanciar a classe por meio da reflexão, o método de reflexão lançará um MemberAccessException.If an attempt is made to instantiate the class by using reflection, the reflection method throws a MemberAccessException.

    Por padrão, uma classe base pode ser instanciada chamando seu construtor de classe.By default, a base class can be instantiated by calling its class constructor. Você não precisa definir um construtor de classe explicitamente.You do not have to explicitly define a class constructor. Se não houver um presente no código-fonte da classe base, o compilador de C# fornecerá automaticamente um construtor (sem parâmetros) padrão.If one is not present in the base class' source code, the C# compiler automatically provides a default (parameterless) constructor.

    No seu exemplo, você marcará a classe Publication como abstract, para que não seja possível criar uma instância dela.For your example, you'll mark the Publication class as abstract so that it cannot be instantiated. Uma classe abstract sem nenhum método abstract indica que essa classe representa um conceito abstrato que é compartilhado entre várias classes concretas (como Book, Journal).An abstract class without any abstract methods indicates that this class represents an abstract concept that is shared among several concrete classes (like a Book, Journal).

  • Se as classes derivadas precisam herdar a implementação de membros específicos da classe base, se elas têm a opção de substituir a implementação da classe base ou se precisam fornecer uma implementação.Whether derived classes must inherit the base class implementation of particular members, whether they have the option to override the base class implementation, or whether they must provide an implementation. Use a palavra-chave abstract para forçar as classes derivadas a fornecer uma implementação.You use the abstract keyword to force derived classes to provide an implementation. Use a palavra-chave virtual para permitir que as classes derivadas substituam um método de classe base.You use the virtual keyword to allow derived classes to override a base class method. Por padrão, os métodos definidos na classe base não são substituíveis.By default, methods defined in the base class are not overridable.

A classe Publication não tem nenhum método abstract, mas a classe em si é abstract.The Publication class does not have any abstract methods, but the class itself is abstract.

  • Se uma classe derivada representa a classe final na hierarquia de herança e não pode se ser usada como uma classe base para outras classes derivadas.Whether a derived class represents the final class in the inheritance hierarchy and cannot itself be used as a base class for additional derived classes. Por padrão, qualquer classe pode servir como classe base.By default, any class can serve as a base class. Você pode aplicar a palavra-chave sealed para indicar que uma classe não pode funcionar como uma classe base para quaisquer classes adicionais.You can apply the sealed keyword to indicate that a class cannot serve as a base class for any additional classes. A tentativa de derivar de uma classe selada gerou o erro do compilador CS0509, "Não é possível derivar do tipo selado <typeName>".Attempting to derive from a sealed class generated compiler error CS0509, "cannot derive from sealed type <typeName>".

    No seu exemplo, você marcará a classe derivada como sealed.For your example, you'll mark your derived class as sealed.

O exemplo a seguir mostra o código-fonte para a classe Publication, bem como uma enumeração PublicationType que é retornada pela propriedade Publication.PublicationType.The following example shows the source code for the Publication class, as well as a PublicationType enumeration that is returned by the Publication.PublicationType property. Além dos membros herdados de Object, a classe Publication define os seguintes membros exclusivos e substituições de membro:In addition to the members that it inherits from Object, the Publication class defines the following unique members and member overrides:

using System;

public enum PublicationType { Misc, Book, Magazine, Article };

public abstract class Publication
{
   private bool published = false;
   private DateTime datePublished;
   private int totalPages; 

   public Publication(string title, string publisher, PublicationType type)
   {
      if (publisher == null)
         throw new ArgumentNullException("The publisher cannot be null.");
      else if (String.IsNullOrWhiteSpace(publisher))
         throw new ArgumentException("The publisher cannot consist only of white space.");
      Publisher = publisher;
  
      if (title == null)
         throw new ArgumentNullException("The title cannot be null.");
      else if (String.IsNullOrWhiteSpace(title))
         throw new ArgumentException("The title cannot consist only of white space.");
      Title = title;

      Type = type;
   }

   public string Publisher { get; }

   public string Title { get; }

   public PublicationType Type { get; }

   public string CopyrightName { get; private set; }
   
   public int CopyrightDate { get; private set; }

   public int Pages
   {
     get { return totalPages; }
     set 
     {
         if (value <= 0)
            throw new ArgumentOutOfRangeException("The number of pages cannot be zero or negative.");
         totalPages = value;   
     }
   }

   public string GetPublicationDate()
   {
      if (!published)
         return "NYP";
      else
         return datePublished.ToString("d");   
   }
   
   public void Publish(DateTime datePublished)
   {
      published = true;
      this.datePublished = datePublished;
   }

   public void Copyright(string copyrightName, int copyrightDate)
   {
      if (copyrightName == null)
         throw new ArgumentNullException("The name of the copyright holder cannot be null.");
      else if (String.IsNullOrWhiteSpace(copyrightName))
         throw new ArgumentException("The name of the copyright holder cannot consist only of white space.");
      CopyrightName = copyrightName;
      
      int currentYear = DateTime.Now.Year;
      if (copyrightDate < currentYear - 10 || copyrightDate > currentYear + 2)
         throw new ArgumentOutOfRangeException($"The copyright year must be between {currentYear - 10} and {currentYear + 1}");
      CopyrightDate = copyrightDate;      
   }

   public override string ToString() => Title;
}
  • Um construtorA constructor

    Como a classe Publication é abstract, sua instância não pode ser criada diretamente no código, com no exemplo a seguir:Because the Publication class is abstract, it cannot be instantiated directly from code like the following example:

    var publication = new Publication("Tiddlywinks for Experts", "Fun and Games",
                                      PublicationType.Book);
    

    No entanto, o construtor de instância pode ser chamado diretamente dos construtores de classe derivada, como mostra o código-fonte para a classe Book.However, its instance constructor can be called directly from derived class constructors, as the source code for the Book class shows.

  • Duas propriedades relacionadas à publicaçãoTwo publication-related properties

    Title é uma propriedade String somente leitura cujo valor é fornecido pela chamada do construtor Publication.Title is a read-only String property whose value is supplied by calling the Publication constructor.

    Pages é uma propriedade Int32 de leitura-gravação que indica o número total de páginas da publicação.Pages is a read-write Int32 property that indicates how many total pages the publication has. O valor é armazenado em um campo privado chamado totalPages.The value is stored in a private field named totalPages. O lançamento deve ser de um número positivo ou de um ArgumentOutOfRangeException.It must be a positive number or an ArgumentOutOfRangeException is thrown.

  • Membros relacionados ao publicadorPublisher-related members

    Duas propriedades somente leitura, Publisher e Type.Two read-only properties, Publisher and Type. Originalmente, os valores eram fornecidos pela chamada para o construtor da classe Publication.The values are originally supplied by the call to the Publication class constructor.

  • Membros relacionados à publicaçãoPublishing-related members

    Dois métodos, Publish e GetPublicationDate, definem e retornam a data de publicação.Two methods, Publish and GetPublicationDate, set and return the publication date. O método Publish define um sinalizador published privado como true quando ele é chamado, e atribui a data passada para ele como um argumento para o campo datePublished privado.The Publish method sets a private published flag to true when it is called and assigns the date passed to it as an argument to the private datePublished field. O método GetPublicationDate retorna a cadeia de caracteres "NYP" se o sinalizador published for false, e o valor do campo datePublished for true.The GetPublicationDate method returns the string "NYP" if the published flag is false, and the value of the datePublished field if it is true.

  • Membros relacionados a direitos autoraisCopyright-related members

    O método Copyright usa o nome do proprietário dos direitos autorais e o ano dos direitos autorais como argumentos e os atribui às propriedades CopyrightName e CopyrightDate.The Copyright method takes the name of the copyright holder and the year of the copyright as arguments and assigns them to the CopyrightName and CopyrightDate properties.

  • Uma substituição do método ToStringAn override of the ToString method

    Se um tipo não substituir o método Object.ToString, ele retornará o nome totalmente qualificado do tipo, que é de pouca utilidade na diferenciação de uma instância para outra.If a type does not override the Object.ToString method, it returns the fully qualified name of the type, which is of little use in differentiating one instance from another. A classe Publication substitui Object.ToString para retornar o valor da propriedade Title.The Publication class overrides Object.ToString to return the value of the Title property.

A figura a seguir ilustra o relacionamento entre a classe base Publication e sua classe herdada implicitamente Object.The following figure illustrates the relationship between your base Publication class and its implicitly inherited Object class.

As classes Object e Publication

A classe BookThe Book class

A classe Book representa um livro como tipo especializado de publicação.The Book class represents a book as a specialized type of publication. O exemplo a seguir mostra o código-fonte para a classe Book.The following example shows the source code for the Book class.

using System;

public sealed class Book : Publication
{
   public Book(string title, string author, string publisher) : 
          this(title, String.Empty, author, publisher)
   { }

   public Book(string title, string isbn, string author, string publisher) : base(title, publisher, PublicationType.Book)
   {
      // isbn argument must be a 10- or 13-character numeric string without "-" characters.
      // We could also determine whether the ISBN is valid by comparing its checksum digit 
      // with a computed checksum.
      //
      if (! String.IsNullOrEmpty(isbn)) {
        // Determine if ISBN length is correct.
        if (! (isbn.Length == 10 | isbn.Length == 13))
            throw new ArgumentException("The ISBN must be a 10- or 13-character numeric string.");
        ulong nISBN = 0;
        if (! UInt64.TryParse(isbn, out nISBN))
            throw new ArgumentException("The ISBN can consist of numeric characters only.");
      } 
      ISBN = isbn;

      Author = author;
   }
     
   public string ISBN { get; }

   public string Author { get; }
   
   public Decimal Price { get; private set; }

   // A three-digit ISO currency symbol.
   public string Currency { get; private set; }
   

   // Returns the old price, and sets a new price.
   public Decimal SetPrice(Decimal price, string currency)
   {
       if (price < 0)
          throw new ArgumentOutOfRangeException("The price cannot be negative.");
       Decimal oldValue = Price;
       Price = price;
       
       if (currency.Length != 3)
          throw new ArgumentException("The ISO currency symbol is a 3-character string.");
       Currency = currency;

       return oldValue;      
   }

   public override bool Equals(object obj)
   {
      Book book = obj as Book;
      if (book == null)
         return false;
      else
         return ISBN == book.ISBN;   
   }

   public override int GetHashCode() => ISBN.GetHashCode();

   public override string ToString() => $"{(String.IsNullOrEmpty(Author) ? "" : Author + ", ")}{Title}"; 
}

Além dos membros herdados de Publication, a classe Book define os seguintes membros exclusivos e substituições de membro:In addition to the members that it inherits from Publication, the Book class defines the following unique members and member overrides:

  • Dois construtoresTwo constructors

    Os dois construtores Book compartilham três parâmetros comuns.The two Book constructors share three common parameters. Dois, title e publisher, correspondem aos parâmetros do construtor Publication.Two, title and publisher, correspond to parameters of the Publication constructor. O terceiro é author, que é armazenado em uma propriedade pública Author imutável.The third is author, which is stored to a public immutable Author property. Um construtor inclui um parâmetro isbn, que é armazenado na propriedade automática ISBN.One constructor includes an isbn parameter, which is stored in the ISBN auto-property.

    O primeiro construtor usa a palavra-chave this para chamar o outro construtor.The first constructor uses the this keyword to call the other constructor. O encadeamento do construtor é um padrão comum na definição de construtores.Constructor chaining is a common pattern in defining constructors. Construtores com menos parâmetros fornecem valores padrão ao chamar o construtor com o maior número de parâmetros.Constructors with fewer parameters provide default values when calling the constructor with the greatest number of parameters.

    O segundo construtor usa a palavra-chave base para passar o título e o nome do publicador para o construtor da classe base.The second constructor uses the base keyword to pass the title and publisher name to the base class constructor. Se você não fizer uma chamada explícita para um construtor de classe base em seu código-fonte, o compilador de C# fornecerá automaticamente uma chamada para a classe base padrão ou para o construtor sem parâmetros.If you don't make an explicit call to a base class constructor in your source code, the C# compiler automatically supplies a call to the base class' default or parameterless constructor.

  • Uma propriedade ISBN somente leitura, que retorna o ISBN (International Standard Book Number) do objeto Book, um número exclusivo com 10 ou 13 dígitos.A read-only ISBN property, which returns the Book object's International Standard Book Number, a unique 10- or 13-digit number. O ISBN é fornecido como um argumento para um dos construtores Book.The ISBN is supplied as an argument to one of the Book constructors. O ISBN é armazenado em um campo de suporte particular, gerado automaticamente pelo compilador.The ISBN is stored in a private backing field, which is auto-generated by the compiler.

  • Uma propriedade Author somente leitura.A read-only Author property. O nome do autor é fornecido como um argumento para os dois construtores Book e é armazenado na propriedade.The author name is supplied as an argument to both Book constructors and is stored in the property.

  • Duas propriedades somente leitura relacionadas ao preço, Price e Currency.Two read-only price-related properties, Price and Currency. Seus valores são fornecidos como argumentos em uma chamada do método SetPrice.Their values are provided as arguments in a SetPrice method call. A propriedade Currency é o símbolo de moeda ISO de três dígitos (por exemplo, USD para dólar americano).The Currency property is the three-digit ISO currency symbol (for example, USD for the U.S. dollar). Símbolos de moeda ISO podem ser obtidos da propriedade ISOCurrencySymbol.ISO currency symbols can be retrieved from the ISOCurrencySymbol property. Ambas as propriedades são somente leitura externamente, mas podem ser definidas por código na classe Book.Both of these properties are externally read-only, but both can be set by code in the Book class.

  • Um método SetPrice, que define os valores das propriedades Price e Currency.A SetPrice method, which sets the values of the Price and Currency properties. Esses valores são retornados por essas mesmas propriedades.Those values are returned by those same properties.

  • Substitui o método ToString (herdado de Publication) e os métodos Object.Equals(Object) e GetHashCode (herdados de Object).Overrides to the ToString method (inherited from Publication) and the Object.Equals(Object) and GetHashCode methods (inherited from Object).

    A menos que seja substituído, o método Object.Equals(Object) testa a igualdade de referência.Unless it is overridden, the Object.Equals(Object) method tests for reference equality. Ou seja, duas variáveis de objeto são consideradas iguais se fizerem referência ao mesmo objeto.That is, two object variables are considered to be equal if they refer to the same object. Na classe Book, por outro lado, dois objetos Book devem ser iguais quando têm o mesmo ISBN.In the Book class, on the other hand, two Book objects should be equal if they have the same ISBN.

    Ao substituir o método Object.Equals(Object), substitua também o método GetHashCode, que retorna um valor usado pelo tempo de execução para armazenar itens em coleções de hash para uma recuperação eficiente.When you override the Object.Equals(Object) method, you must also override the GetHashCode method, which returns a value that the runtime uses to store items in hashed collections for efficient retrieval. O código de hash deve retornar um valor que é consistente com o teste de igualdade.The hash code should return a value that's consistent with the test for equality. Como você substituiu Object.Equals(Object) para retornar true, se as propriedades de ISBN de dois objetos Book forem iguais, retorne o código hash computado chamando o método GetHashCode da cadeia de caracteres retornada pela propriedade ISBN.Since you've overridden Object.Equals(Object) to return true if the ISBN properties of two Book objects are equal, you return the hash code computed by calling the GetHashCode method of the string returned by the ISBN property.

A figura a seguir ilustra o relacionamento entre a classe Book e Publication, sua classe base.The following figure illustrates the relationship between the Book class and Publication, its base class.

Classes Publication e Book

Agora você pode criar a instância de um objeto Book, invocar seus membros exclusivos e herdados e passá-lo como um argumento a um método que espera um parâmetro do tipo Publication ou do tipo Book, como mostra o exemplo a seguir.You can now instantiate a Book object, invoke both its unique and inherited members, and pass it as an argument to a method that expects a parameter of type Publication or of type Book, as the following example shows.

using System;
using static System.Console;

public class Example
{
   public static void Main()
   {
      var book = new Book("The Tempest",  "0971655819", "Shakespeare, William",
                          "Public Domain Press");
      ShowPublicationInfo(book);
      book.Publish(new DateTime(2016, 8, 18));
      ShowPublicationInfo(book);

      var book2 = new Book("The Tempest", "Classic Works Press", "Shakespeare, William");
      Write($"{book.Title} and {book2.Title} are the same publication: " +
            $"{((Publication) book).Equals(book2)}");
   }

   public static void ShowPublicationInfo(Publication pub)
   {
       string pubDate = pub.GetPublicationDate();
       WriteLine($"{pub.Title}, " +
                 $"{(pubDate == "NYP" ? "Not Yet Published" : "published on " + pubDate):d} by {pub.Publisher}"); 
   }
}
// The example displays the following output:
//        The Tempest, Not Yet Published by Public Domain Press
//        The Tempest, published on 8/18/2016 by Public Domain Press
//        The Tempest and The Tempest are the same publication: False

Criando classes base abstratas e suas classes derivadasDesigning abstract base classes and their derived classes

No exemplo anterior, você definiu uma classe base que forneceu uma implementação de diversos métodos para permitir que classes derivadas compartilhem código.In the previous example, you defined a base class that provided an implementation for a number of methods to allow derived classes to share code. Em muitos casos, no entanto, não espera-se que a classe base forneça uma implementação.In many cases, however, the base class is not expected to provide an implementation. Nesse caso, a classe base é uma classe abstrata que declara métodos abstratos. Ela funciona como um modelo que define os membros que cada classe derivada precisa implementar.Instead, the base class is an abstract class that declares abstract methods; it serves as a template that defines the members that each derived class must implement. Normalmente em uma classe base abstrata, a implementação de cada tipo derivado é exclusiva para esse tipo.Typically in an abstract base class, the implementation of each derived type is unique to that type. Você marcou a classe com a palavra-chave abstract porque não fazia sentido criar uma instância de um objeto Publication, embora a classe fornecesse implementações de funcionalidades comuns para publicações.You marked the class with the abstract keyword because it made no sense to instantiate a Publication object, although the class did provide implementations of functionality common to publications.

Por exemplo, cada forma geométrica bidimensional fechada inclui duas propriedades: área, a extensão interna da forma; e perímetro, ou a distância entre as bordas da forma.For example, each closed two-dimensional geometric shape includes two properties: area, the inner extent of the shape; and perimeter, or the distance along the edges of the shape. A maneira com a qual essas propriedades são calculadas, no entanto, depende completamente da forma específica.The way in which these properties are calculated, however, depends completely on the specific shape. A fórmula para calcular o perímetro (ou a circunferência) de um círculo, por exemplo, é diferente da fórmula de um triângulo.The formula for calculating the perimeter (or circumference) of a circle, for example, is different from that of a triangle. A classe Shape é uma classe abstract com métodos abstract.The Shape class is an abstract class with abstract methods. Isso indica que as classes derivadas compartilham a mesma funcionalidade, mas essas classes derivadas implementam essa funcionalidade de forma diferente.That indicates derived classes share the same functionality, but those derived classes implement that functionality differently.

O exemplo a seguir define uma classe base abstrata denominada Shape que define duas propriedades: Area e Perimeter.The following example defines an abstract base class named Shape that defines two properties: Area and Perimeter. Além da classe ser marcada com a palavra-chave abstract, cada membro da instância também é marcado com a palavra-chave abstract.In addition to marking the class with the abstract keyword, each instance member is also marked with the abstract keyword. Nesse caso, o Shape também substitui o método Object.ToString para retornar o nome do tipo, em vez de seu nome totalmente qualificado.In this case, Shape also overrides the Object.ToString method to return the name of the type, rather than its fully qualified name. E define dois membros estáticos, GetArea e GetPerimeter, que permitem a recuperação fácil da área e do perímetro de uma instância de qualquer classe derivada.And it defines two static members, GetArea and GetPerimeter, that allow callers to easily retrieve the area and perimeter of an instance of any derived class. Quando você passa uma instância de uma classe derivada para um desses métodos, o tempo de execução chama a substituição do método da classe derivada.When you pass an instance of a derived class to either of these methods, the runtime calls the method override of the derived class.

using System;

public abstract class Shape
{
   public abstract double Area { get; }
   
   public abstract double Perimeter { get; }
 
   public override string ToString() => GetType().Name;

   public static double GetArea(Shape shape) => shape.Area;

   public static double GetPerimeter(Shape shape) => shape.Perimeter;
}

Em seguida, você pode derivar algumas classes de Shape que representam formas específicas.You can then derive some classes from Shape that represent specific shapes. O exemplo a seguir define três classes, Triangle, Rectangle e Circle.The following example defines three classes, Triangle, Rectangle, and Circle. Cada uma usa uma fórmula exclusiva para essa forma específica para calcular a área e o perímetro.Each uses a formula unique for that particular shape to compute the area and perimeter. Algumas das classes derivadas também definem propriedades, como Rectangle.Diagonal e Circle.Diameter, que são exclusivas para a forma que representam.Some of the derived classes also define properties, such as Rectangle.Diagonal and Circle.Diameter, that are unique to the shape that they represent.

using System;

public class Square : Shape
{
   public Square(double length)
   {
      Side = length;
   }

   public double Side { get; }  

   public override double Area => Math.Pow(Side, 2); 

   public override double Perimeter => Side * 4;

   public double Diagonal => Math.Round(Math.Sqrt(2) * Side, 2); 
}

public class Rectangle : Shape
{
   public Rectangle(double length, double width)
   {
      Length = length;
      Width = width;
   }

   public double Length { get; }

   public double Width { get; }

   public override double Area => Length * Width;

   public override double Perimeter => 2 * Length + 2 * Width;  

   public bool IsSquare() => Length == Width;

   public double Diagonal => Math.Round(Math.Sqrt(Math.Pow(Length, 2) + Math.Pow(Width, 2)), 2); 
}

public class Circle : Shape
{
   public Circle(double radius)
   {
      Radius = radius;
   } 

   public override double Area => Math.Round(Math.PI * Math.Pow(Radius, 2), 2); 

   public override double Perimeter => Math.Round(Math.PI * 2 * Radius, 2); 

   // Define a circumference, since it's the more familiar term.
   public double Circumference => Perimeter; 

   public double Radius { get; }

   public double Diameter => Radius * 2; 
}

O exemplo a seguir usa objetos derivados de Shape.The following example uses objects derived from Shape. Ele cria uma matriz de objetos derivados de Shape e chama os métodos estáticos da classe Shape, que retorna valores de propriedade Shape.It instantiates an array of objects derived from Shape and calls the static methods of the Shape class, which wraps return Shape property values. O tempo de execução recupera os valores das propriedades substituídas dos tipos derivados.The runtime retrieves values from the overridden properties of the derived types. O exemplo também converte cada objeto Shape na matriz ao seu tipo derivado e, se a conversão for bem-sucedida, recupera as propriedades dessa subclasse específica de Shape.The example also casts each Shape object in the array to its derived type and, if the cast succeeds, retrieves properties of that particular subclass of Shape.

using System;

public class Example
{
   public static void Main()
   {
      Shape[] shapes = { new Rectangle(10, 12), new Square(5),
                        new Circle(3) };
      foreach (var shape in shapes) {
         Console.WriteLine($"{shape}: area, {Shape.GetArea(shape)}; " +
                           $"perimeter, {Shape.GetPerimeter(shape)}");
         var rect = shape as Rectangle;
         if (rect != null) {
            Console.WriteLine($"   Is Square: {rect.IsSquare()}, Diagonal: {rect.Diagonal}");
            continue;
         }
         var sq = shape as Square;
         if (sq != null) {
            Console.WriteLine($"   Diagonal: {sq.Diagonal}");
            continue;
         }
      }   
   }
}
// The example displays the following output:
//         Rectangle: area, 120; perimeter, 44
//            Is Square: False, Diagonal: 15.62
//         Square: area, 25; perimeter, 20
//            Diagonal: 7.07
//         Circle: area, 28.27; perimeter, 18.85

Consulte tambémSee also