Обучение
Схема обучения
Реализация наследования и полиморфизма - Training
Реализация наследования и полиморфизма
Этот браузер больше не поддерживается.
Выполните обновление до Microsoft Edge, чтобы воспользоваться новейшими функциями, обновлениями для системы безопасности и технической поддержкой.
Примечание
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Полиморфизм часто называется третьим столпом объектно-ориентированного программирования после инкапсуляции и наследования. Полиморфизм — слово греческого происхождения, означающее "многообразие форм" и имеющее несколько аспектов.
Виртуальные методы позволяют работать с группами связанных объектов универсальным способом. Представим, например, приложение, позволяющее пользователю создавать различные виды фигур на поверхности для рисования. Во время компиляции не известно, какие типы фигур будут создаваться пользователем. При этом приложению необходимо отслеживать все различные типы создаваемых фигур и обновлять их в ответ на движения мыши. Для решения этой проблемы можно использовать полиморфизм, выполнив два основных действия.
Для начала создайте базовый класс с именем Shape
и производные классы, например Rectangle
, Circle
и Triangle
. Присвойте классу Shape
виртуальный метод с именем Draw
и переопределите его в каждом производном классе для рисования конкретной фигуры, которую этот класс представляет. Создайте объект List<Shape>
и добавьте в него Circle
, Triangle
и Rectangle
.
public class Shape
{
// A few example members
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; }
// Virtual method
public virtual void Draw()
{
Console.WriteLine("Performing base class drawing tasks");
}
}
public class Circle : Shape
{
public override void Draw()
{
// Code to draw a circle...
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
public class Rectangle : Shape
{
public override void Draw()
{
// Code to draw a rectangle...
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
public class Triangle : Shape
{
public override void Draw()
{
// Code to draw a triangle...
Console.WriteLine("Drawing a triangle");
base.Draw();
}
}
Для обновления поверхности рисования используйте цикл foreach, чтобы выполнить итерацию списка и вызвать метод Draw
на каждом объекте Shape
в списке. Несмотря на то, что каждый объект в списке имеет объявленный тип Shape
, будет вызван тип времени выполнения (переопределенная версия метода в каждом производном классе).
// Polymorphism at work #1: a Rectangle, Triangle and Circle
// can all be used wherever a Shape is expected. No cast is
// required because an implicit conversion exists from a derived
// class to its base class.
var shapes = new List<Shape>
{
new Rectangle(),
new Triangle(),
new Circle()
};
// Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (var shape in shapes)
{
shape.Draw();
}
/* Output:
Drawing a rectangle
Performing base class drawing tasks
Drawing a triangle
Performing base class drawing tasks
Drawing a circle
Performing base class drawing tasks
*/
В C# каждый тип является полиморфным, так как все типы, включая пользовательские, наследуют Object.
Если производный класс наследует от базового класса, он включает все члены базового класса. Все поведение, объявленное в базовом классе, является частью производного класса. Это позволяет рассматривать объекты производного класса как объекты базового класса. Модификаторы доступа (public
и protected
private
т. д.) определяют, доступны ли эти члены из реализации производного класса. Виртуальные методы предоставляют конструктору различные варианты поведения производного класса:
Производный класс может переопределить член базового класса, только если последний будет объявлен виртуальным или абстрактным. Производный член должен использовать ключевое слово override, указывающее, что метод предназначен для участия в виртуальном вызове. Примером является следующий код:
public class BaseClass
{
public virtual void DoWork() { }
public virtual int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public override void DoWork() { }
public override int WorkProperty
{
get { return 0; }
}
}
Поля не могут быть виртуальными; только методы, свойства, события и индексаторы могут быть виртуальными. Когда производный класс переопределяет виртуальный член, он вызывается даже в то случае, если доступ к экземпляру этого класса осуществляется в качестве экземпляра базового класса. Примером является следующий код:
DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.
BaseClass A = B;
A.DoWork(); // Also calls the new method.
Виртуальные методы и свойства позволяют производным классам расширять базовый класс без необходимости использовать реализацию базового класса метода. Дополнительные сведения см. в разделе Управление версиями с помощью ключевых слов Override и New. Еще одну возможность определения метода или набора методов, реализация которых оставлена производным классам, дает интерфейс.
Если вы хотите, чтобы производный класс имел член с тем же именем, что и член в базовом классе, можно использовать ключевое слово new, чтобы скрыть член базового класса. Ключевое слово new
вставляется перед типом возвращаемого значения замещаемого члена класса. Примером является следующий код:
public class BaseClass
{
public void DoWork() { WorkField++; }
public int WorkField;
public int WorkProperty
{
get { return 0; }
}
}
public class DerivedClass : BaseClass
{
public new void DoWork() { WorkField++; }
public new int WorkField;
public new int WorkProperty
{
get { return 0; }
}
}
При использовании ключевого new
слова создается метод, который скрывает метод базового класса, а не переопределяет его. Это отличается от виртуальных методов. При скрытии метода вызываемый метод зависит от типа переменной во время компиляции, а не типа времени выполнения объекта.
Скрытые члены базового класса можно получить из клиентского кода, приведя экземпляр производного класса к экземпляру базового класса. Например:
DerivedClass B = new DerivedClass();
B.DoWork(); // Calls the new method.
BaseClass A = (BaseClass)B;
A.DoWork(); // Calls the old method.
В этом примере оба переменных ссылаются на один и тот же экземпляр объекта, но метод, который вызывается, зависит от объявленного типа переменной: DerivedClass.DoWork()
при доступе DerivedClass
через переменную и BaseClass.DoWork()
при доступе BaseClass
через переменную.
Виртуальные члены остаются виртуальными независимо от количества классов, объявленных между виртуальным членом и классом, который объявил его изначально. Если класс A
объявляет виртуальный член, класс B
является производным от класса A
, а класс C
— от класса B
, то класс C
наследует виртуальный член и может переопределить его независимо от того, объявляет ли класс B
переопределение этого члена. Примером является следующий код:
public class A
{
public virtual void DoWork() { }
}
public class B : A
{
public override void DoWork() { }
}
Производный класс может остановить виртуальное наследование, объявив переопределение как запечатанное. Для остановки наследования в объявление члена класса нужно вставить ключевое слово sealed
перед ключевым словом override
. Примером является следующий код:
public class C : B
{
public sealed override void DoWork() { }
}
В предыдущем примере метод DoWork
более не является виртуальным ни для одного класса, производного от класса C
. Он по-прежнему является виртуальным для экземпляров класса C
, даже если они приводятся к типу B
или типу A
. Запечатанные методы можно заменить производными классами с помощью ключевого слова new
, как показано в следующем примере:
public class D : C
{
public new void DoWork() { }
}
В этом случае, если DoWork
вызывается для D
с помощью переменной типа D
, вызывается новый DoWork
. Если переменная типа C
, B
или A
используется для доступа к экземпляру D
, вызов DoWork
будет выполняться по правилам виртуального наследования и направлять эти вызовы в реализацию DoWork
в классе C
.
Производный класс, который заменил или переопределил метод или свойство, может получить доступ к методу или свойству на базовом классе с помощью ключевого слова base
. Примером является следующий код:
public class Base
{
public virtual void DoWork() {/*...*/ }
}
public class Derived : Base
{
public override void DoWork()
{
//Perform Derived's work here
//...
// Call DoWork on base class
base.DoWork();
}
}
Для получения дополнительной информации см. базу.
Примечание
Рекомендуется, чтобы виртуальные члены использовали base
для вызова реализации базового класса этого члена в их собственной реализации. Разрешение поведения базового класса позволяет производному классу концентрироваться на реализации поведения, характерного для производного класса. Если реализация базового класса не вызывается, производный класс сопоставляет свое поведение с поведением базового класса по своему усмотрению.
Отзыв о .NET
.NET — это проект с открытым исходным кодом. Выберите ссылку, чтобы оставить отзыв:
Обучение
Схема обучения
Реализация наследования и полиморфизма - Training
Реализация наследования и полиморфизма
События