Наследование (Руководство по программированию в C#)

Обновлен: Июль 2008

Наследование, вместе с инкапсуляцией и полиморфизмом, является одной из трех основных характеристик (или базовых идей) объектно-ориентированного программирования. Наследование позволяет создавать новые классы, которые повторно используют, расширяют и изменяют поведение, определенное в других классах. Класс, члены которого наследуются, называется базовым классом, а класс, который наследует эти члены, называется производным классом.

ms173149.alert_note(ru-ru,VS.90).gifПримечание.

Структуры не поддерживают наследование, но они могут реализовывать интерфейсы. Дополнительные сведения см. в разделе Интерфейсы (Руководство по программированию в C#).

Концептуально, производный класс является специализацией базового класса. Например, при наличии базового класса Animal, возможно наличие одного производного класса, который называется Mammal, и еще одного производного класса, который называется Reptile. Mammal является Animal, а Reptile является Animal, но каждый производный класс представляет разные специализации базового класса.

При определении класса для наследования от другого класса, производный класс явно получает все члены базового класса, за исключением его конструкторов и деструкторов. Производный класс может таким образом повторно использовать код в базовом классе без необходимости в его повторной реализации. В производном классе можно добавить больше членов. Таким образом, производный класс расширяет функциональность базового класса.

Ниже иллюстрируется класс WorkItem, представляющий рабочий элемент в бизнес-процессе. Как и другие классы, он является производным от System.Object и наследует все его методы. WorkItem добавляет пять собственных членов. К ним относится конструктор, поскольку конструкторы не наследуются. ChangeRequest наследует от WorkItem и представляет определенный тип рабочего элемента. ChangeRequest добавляет еще два члена к членам, которые он наследует от WorkItem и Object. Он должен добавить свой собственный конструктор, и также он добавляет член, который позволит ChangeRequest быть связанным с исходным WorkItem, к которому применимо изменение.

Наследование классов
Наследование классов

В следующем рисунке показано, как выражаются в C# отношения между классами, продемонстрированные в предыдущем примере. В следующем примере также показано, как WorkItem переопределяет виртуальный метод Object.ToString, и как класс ChangeRequest наследует реализацию WorkItem метода.

// 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
*/

Абстрактные и виртуальные методы

Когда базовый класс объявляет метод как виртуальный, производный класс может переопределить метод с помощью своей собственной реализации. Если базовый класс объявляет член как абстрактный, то этот метод должен быть переопределен в любом неабстрактном классе, который прямо наследует от этого класса. Если производный класс сам является абстрактным, то он наследует абстрактные члены, не реализуя их. Абстрактные и виртуальные члены являются основой для полиморфизма, который является второй основной характеристикой объектно-ориентированного программирования. Дополнительные сведения см. в разделе Полиморфизм (руководство по программированию в C#).

Абстрактные базовые классы

Можно объявить класс как абстрактный, если необходимо предотвратить прямое создание экземпляров посредством нового ключевого слова. При таком подходе класс можно использовать, только если новый класс является производным от него. Абстрактный класс может содержать один или несколько подписей методов, которые сами объявлены в качестве абстрактных. Эти подписи задают параметры и возвращают значение, но не имеют реализации (тела метода). Абстрактному классу необязательно содержать абстрактные члены; однако, если класс все же содержит абстрактный член, то сам класс должен быть объявлен в качестве абстрактного. Производные классы, которые сами не являются абстрактными, должны предоставить реализацию для любых абстрактных методов из абстрактного базового класса. Дополнительные сведения см. в разделах Абстрактные и запечатанные классы и члены классов (руководство по программированию в C#) и Разработка абстрактных классов.

Интерфейсы

интерфейс является ссылочным типом, в чет-то схожим с абстрактным базовым классом, который состоит только из абстрактных членов. Когда класс наследует от интерфейса, он должен предоставить реализацию для всех членов интерфейса. В классе может быть реализовано несколько интерфейсов, хотя производным он может быть только от одного прямого базового класса.

Интерфейсы используются для определения определенных возможностей для классов, которые не обязательно имеют отношения тождественности. Например, интерфейс IEquatable[`1] может быть реализован любым классом или структурой, которой необходимо разрешить клиентскому коду определить, являются ли два объекта типа эквивалентными (однако тип определяет эквивалентность). IEquatable<T> не подразумевает тот же вид отношений тождественности, который существует между базовым и производным классами (например, Mammal является Animal). Дополнительные сведения см. в разделе Интерфейсы (Руководство по программированию в C#).

Доступ производного класса к членам базового класса

Из производного класса можно получить доступ к открытым, защищенным, внутренним и защищенным внутренним членам базового класса. Хотя производный класс и наследует закрытые члены базового класса, он не может получить доступ к этим членам. Однако все эти закрытые члены все же присутствуют в производном классе и могут выполнять ту же работу, что и в самом базовом классе. Например, предположим, что защищенный метод базового класса имеет доступ к закрытому полю. Это поле должно присутствовать в производном классе для надлежащей работы унаследованного метода базового класса.

Предотвращение дальнейшего наследования

Класс может предотвратить наследование от него других классов или наследование от любых его членов, объявив себя или члены запечатанными. Дополнительные сведения см. в разделе Абстрактные и запечатанные классы и члены классов (руководство по программированию в C#).

Скрытие производного класса членов базового класса

Производный класс может скрывать члены базового класса путем объявления членов с тем же именем и подписью. Модификатор new может использоваться, чтобы явно указать, что член не предназначен, чтобы быть переопределением базового члена. Использование new не является обязательным, но при отсутствии использования new будет сгенерировано предупреждение компилятора. Дополнительные сведения см. в разделах Практическое руководство. Управление версиями с помощью ключевых слов "Override" и "New" (Руководство по программированию в C#) и Использование ключевых слов "Override" и "New" (Руководство по программированию в C#).

См. также

Основные понятия

Руководство по программированию в C#

Ссылки

Классы и структуры (Руководство по программированию в C#)

класс (Справочник по C#)

struct (справочник по C#)

Журнал изменений

Дата

Журнал изменений

Причина

Июль 2008

Добавлены содержимое, иллюстрация и новые примеры.

Улучшение информации.