Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Наследование вместе с инкапсуляцией и полиморфизмом является одной из трех основных характеристик объектно-ориентированного программирования. Наследование позволяет создавать новые классы, которые повторно используют, расширяют и изменяют поведение, определенное в других классах. Класс, члены которого наследуются, называют базовым классом, а класс, наследующий эти элементы, называется производным классом. Производный класс может иметь только один прямой базовый класс. Однако наследование является транзитивным. Если ClassC
происходит от ClassB
, а ClassB
происходит от ClassA
, то ClassC
наследует члены, объявленные в ClassB
и ClassA
.
Замечание
Структуры не поддерживают наследование, но могут реализовывать интерфейсы.
Концептуально производный класс является специализацией базового класса. Например, если у вас есть базовый класс Animal
, у вас может быть один производный класс, который называется Mammal
Reptile
и другой производный класс.
Mammal
является Animal
, а Reptile
является Animal
, но каждый производный класс представляет разные специализации базового класса.
Объявления интерфейса могут определять реализацию по умолчанию для своих членов. Эти реализации наследуются производными интерфейсами и классами, реализующими эти интерфейсы. Дополнительные сведения о методах интерфейса по умолчанию см. в статье об интерфейсах.
При определении класса как производного другого класса, производный класс автоматически получает все элементы базового класса, за исключением его конструкторов и финализаторов. Производный класс повторно использует код в базовом классе без необходимости повторного выполнения. Вы можете добавить дополнительные элементы в производный класс. Производный класс расширяет функциональные возможности базового класса.
На следующем рисунке показан класс WorkItem
, представляющий элемент работы в некоторых бизнес-процессах. Как и все классы, он происходит от System.Object и наследует все его методы.
WorkItem
добавляет шесть своих членов. Эти члены включают конструктор, поскольку конструкторы не передаются по наследству. Класс ChangeRequest
наследует от WorkItem
и представляет определенный вид рабочего элемента.
ChangeRequest
добавляет еще два члена к тем, которые он наследует от WorkItem
и Object. Он должен добавить собственный конструктор, а также должен добавить originalItemID
. Свойство originalItemID
позволяет ChangeRequest
экземпляру связываться с оригиналом WorkItem
, к которому применяется запрос на изменение.
В следующем примере показано, как отношения классов, показанные на предыдущем рисунке, выражаются в C#. В примере также показано, как WorkItem
переопределяет виртуальный метод Object.ToString, и как класс ChangeRequest
наследует реализацию метода WorkItem
. Первый блок определяет классы:
// 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;
}
}
В следующем блоке показано, как использовать базовые и производные классы:
// 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
*/
Абстрактные и виртуальные методы
Если базовый класс объявляет метод как virtual
, производный класс может override
метод с собственной реализацией. Если базовый класс объявляет член как abstract
, этот метод должен быть переопределен в любом не абстрактном классе, который напрямую наследует от этого класса. Если производный класс является абстрактным, он наследует абстрактные члены без их реализации. Абстрактные и виртуальные члены являются основой полиморфизма, которая является второй основной характеристикой объектно-ориентированного программирования. Дополнительные сведения см. в разделе Полиморфизм.
Абстрактные базовые классы
Класс можно объявить абстрактным , если вы хотите предотвратить прямое создание экземпляров с помощью нового оператора. Абстрактный класс можно использовать только в том случае, если новый класс является производным от него. Абстрактный класс может содержать одну или несколько подписей методов, которые сами объявляются как абстрактные. Эти сигнатуры указывают параметры и возвращаемое значение, но не имеют реализации (текст метода). Абстрактный класс не должен содержать абстрактные члены; Однако если класс содержит абстрактный член, сам класс должен быть объявлен как абстрактный. Производные классы, не абстрактные сами по себе, должны предоставлять реализацию для абстрактных методов из абстрактного базового класса.
Интерфейсы
Интерфейс — это ссылочный тип, определяющий набор элементов. Все классы и структуры, реализующие этот интерфейс, должны реализовать этот набор элементов. Интерфейс может определить реализацию по умолчанию для любого или всех этих элементов. Класс может реализовать несколько интерфейсов, даже если он может быть производным только от одного прямого базового класса.
Интерфейсы используются для определения конкретных функций или возможностей для классов, которые не обязательно связаны отношением типа "является". Например, интерфейс можно реализовать любым классом или структурой, System.IEquatable<T> чтобы определить, эквивалентны ли два объекта типа (однако тип определяет эквивалентность).
IEquatable<T> не подразумевает того же рода отношения «является», которое существует между базовым классом и производным классом (например, Mammal
— это Animal
). Дополнительные сведения см. в разделе "Интерфейсы".
Предотвращение дальнейшего получения
Класс может запретить другим классам наследовать от него или от любого из его элементов, объявив себя или элемент как sealed
.
Скрытие членов базового класса членами производного класса
Производный класс может скрывать элементы базового класса, объявляя элементы с одинаковым именем и подписью.
new
Модификатор можно использовать явным образом, чтобы указать, что элемент не предназначен для переопределения базового элемента. Использование new
не является обязательным, но предупреждение компилятора будет создано, если new
не используется. Чтобы узнать больше, см. версионирование с ключевыми словами Override и New и когда использовать ключевые слова Override и New.