
- •Лекция . Процедуры и функции - функциональные модули и методы класса
- •Процедуры и функции. Отличия
- •Описание методов. Синтаксис
- •Использование модификаторов ref и out для ссылок на объекты
- •Вызов метода. Синтаксис
- •О соответствии списков формальных и фактических аргументов
- •Вызов метода. Семантика
- •Что нужно знать о методах?
- •Перегрузка методов
- •Полиморфизм. Виртуальные методы
- •Скрытие членов базового класса с новыми членами
- •Предотвращение переопределения виртуальных членов производными классами
- •Индексаторы
Полиморфизм. Виртуальные методы
Полиморфизм предоставляет подклассу способ определения собственной версии метода, определенного в его базовом классе, с использованием процесса, который называется переопределением метода (method overriding). Чтобы пересмотреть текущий дизайн, нужно понять значение ключевых слов virtual и override.
Виртуальнымназывается такой метод, который объявляется какvirtualв базовом классе. Виртуальный метод отличается тем, что он может быть переопределен в одном или нескольких производных классах. Следовательно, у каждого производного класса может быть свой вариант виртуального метода. Кроме того, виртуальные методы интересны тем, что именно происходит при их вызове по ссылке на базовый класс. В этом случае средствами языка С# определяется именно тот вариант виртуального метода, который следует вызывать, исходя из типа объекта, к которому происходит обращение по ссылке, причем это делается во время выполнения. Поэтому при ссылке на разные типы объектов выполняются разные варианты виртуального метода. Иными словами, вариант выполняемого виртуального метода выбирается по типу объекта, а не по типу ссылки на этот объект.
Так, если базовый класс содержит виртуальный метод и от него получены производные классы, то при обращении к разным типам объектов по ссылке на базовый класс выполняются разные варианты этого виртуального метода.
Метод объявляется как виртуальный в базовом классе с помощью ключевого слова virtual, указываемого перед его именем. Когда же виртуальный метод переопределяется в производном классе, то для этого используется модификатор override. А сам процесс повторного определения виртуального метода в производном классе называется переопределением метода. При переопределении имя, возвращаемый тип и сигнатура переопределяющего метода должны быть точно такими же, как и у того виртуального метода, который переопределяется. Кроме того, виртуальный метод не может быть объявлен как static или abstract.
Переопределение метода служит основанием для воплощения одного из самых эффективных в С# принципов: динамической диспетчеризации методов, которая представляет собой механизм разрешения вызова во время выполнения, а не компиляции. Значение динамической диспетчеризации методов состоит в том, что именно благодаря ей в С# реализуется динамический полиморфизм.
Если при наличии многоуровневой иерархии виртуальный метод не переопределяется в производном классе, то выполняется ближайший его вариант, обнаруживаемый вверх по иерархии.
И еще одно замечание: свойства также подлежат модификации ключевым словом virtual и переопределению ключевым словом override. Это же относится и к индексаторам.
Visual Studio 2008О полиморфизме часто говорят как о третьем базовом элементе объектно-ориентированного программирования, после инкапсуляции и наследования. Полиформизм — это греческое слово, означающее "наличие многих форм". Это понятие имеет два различающихся аспекта.
Во время выполнения объекты производного класса могут рассматриваться как объекты базового класса в таких местах как параметры метода и коллекции массивов. При этом объявленный тип объекта больше не идентичен его типу времени выполнения.
Базовые классы могут определять и реализовывать виртуальныеметоды, а производные классы могутпереопределятьих. Это означает, что они предоставляют свои собственные определение и реализацию. Во время выполнения, когда клиентский код вызывает метод, среда CLR ищет тип времени выполнения объекта и вызывает это переопределение виртуального метода. Таким образом, в исходном коде можно вызвать метод в базовом классе и вызвать выполнение метода с версией производного класса.
Виртуальные методы позволяют единым образом работать с группами связанных объектов. Например, предположим, имеется приложение рисования, которое дает возможность пользователю создавать на поверхности рисования различные формы. Во время компиляции неизвестно, какие конкретные формы будет создавать пользователь. Но приложение должно учитывать все различные типы создаваемых форм, и оно должно обновлять их в ответ на действия мыши пользователя. Полиморфизм можно использовать для решения этой проблемы в двух основных этапах.
Создание иерархии классов, в которой класс каждой конкретной формы производится от общего базового класса.
Использование виртуального метода для вызова соответствующего метода в каком-либо производном классе одним вызовом метода базового класса.
Во-первых,
создайте базовый класс, называемый
Shape
,
и производные классы, напримерRectangle
,Circle
иTriangle
.
Предоставьте классуShape
виртуальный метод, называемыйDraw
,
и переопределите его в каждом производном
классе для рисования конкретной формы,
которую представляет класс. Создайте
объектList<Shape>
и добавьте в него круг, треугольник и
прямоугольник. Для обновления поверхности
рисования используйте циклforeachдля итерации списка и вызова методаDraw
на каждом объектеShape
в списке. Хотя каждый объект в списке
имеет объявленный типShape
,
будет вызываться именно тип времени
выполнения (переопределенная версия
метода в каждом производном классе).
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");
}
}
class Circle : Shape
{
public override void Draw()
{
// Code to draw a circle...
Console.WriteLine("Drawing a circle");
base.Draw();
}
}
class Rectangle : Shape
{
public override void Draw()
{
// Code to draw a rectangle...
Console.WriteLine("Drawing a rectangle");
base.Draw();
}
}
class Triangle : Shape
{
public override void Draw()
{
// Code to draw a triangle...
Console.WriteLine("Drawing a triangle");
base.Draw();
}
}
class Program
{
static void Main(string[] args)
{
// Polymorphism at work #1: a Rectangle, Triangle and Circle
// can all be used whereever a Shape is expected. No cast is
// required because an implicit conversion exists from a derived
// class to its base class.
System.Collections.Generic.List<Shape> shapes = new System.Collections.Generic.List<Shape>();
shapes.Add(new Rectangle());
shapes.Add(new Triangle());
shapes.Add(new Circle());
// Polymorphism at work #2: the virtual method Draw is
// invoked on each of the derived classes, not the base class.
foreach (Shape s in shapes)
{
s.Draw();
}
// Keep the console open in debug mode.
Console.WriteLine("Press any key to exit.");
Console.ReadKey();
}
}
/* 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.
Если
производный класс наследует от базового
класса, то он приобретает все методы,
поля, свойства и события базового класса.
Проектировщик производного класса
может выбирать из следующих возможностей:
переопределить виртуальные члены в базовом классе,
наследовать самый близкий метод базового класса без его переопределения,
определить новую не виртуальную реализацию этих членов, которая скрывает реализации базового класса.
Производный класс может переопределить член базового класса, если только член базового класса объявлен как виртуальныйилиабстрактный. Производный член должен использовать ключевое слово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 = (BaseClass)B;
A.DoWork(); // Also calls the new method.
Виртуальные методы и свойства дают возможность производным классам расширять базовый класс, без необходимости использования реализации метода базового класса. Дополнительные сведения см. в разделе Практическое руководство. Управление версиями с помощью ключевых слов "Override" и "New" (Руководство по программированию в C#). Интерфейс предоставляет другой способ определения метода или установки методов, реализация которых предоставлена производным классам. Дополнительные сведения см. в разделахИнтерфейсы (Руководство по программированию в C#)иВыбор между классами и интерфейсами.