- •1. Понятие информационной системы (ис). Корпоративные ис. Структура ис.
- •2. Понятие информационной системы (ис). Корпоративные ис. Классификация ис.
- •3. История развития ис.
- •4. .Net Framework. Общеязыковая среда исполнения (clr). Управляемые модули и сборки.
- •5. .Net Framework. Общая система типов (cts) и Общеязыковая спецификация (cls).
- •6. .Net Framework. Механизм сборки мусора.
- •7. C#. Объявление класса.
- •8. Java. Объявление класса.
- •9. C#. Делегаты.
- •10. C#. События.
- •11. C#. Наследование: правила, синтаксис. Сокрытие имен.
- •12. Java. Наследование: правила, синтаксис. Сокрытие имен.
- •13. Правила наследования типов. Принцип «Open-Closed Principle».
- •14. Правила наследования типов. Принцип «Liskov Substitution Principle».
- •15. C#. Приведение типов. Операторы as и is.
- •16. C#. Виртуальные и невиртуальные методы. Статические методы.
- •17. C#. Абстрактные и конечные методы.
- •310 Часть I. Язык с#
- •18. Java. Абстрактные, виртуальные, конечные и статические методы.
- •19. C# и Java. Интерфейсы.
- •20.C#. Структуры.
- •21. C#. Перечисления.
- •22.C#. Упаковка и распаковка объектов значимых типов.
- •23.C# и Java. Исключения. Механизм исключений.
- •24.C# и Java. Исключения. Классы исключений.
- •25.C# и Java. Исключения. Основные принципы использования механизма
- •26.C#. Обобщения: понятие и синтаксис. Ограничения обобщений.
- •27. Java. Обобщения: понятие и синтаксис.
- •28.C#. Коллекции.
- •29.Java. Коллекции.
- •30.C#. Dispose Pattern.
- •31. .Net Framework 3.0, 3.5, 3.5 sp1: общая характеристика.
- •32. C# 3.0, linq: обзор.
- •Источники данных
- •SqlMetal
- •33. Понятие жизненного цикла ис (жц ис). Каскадная модель жц ис.
- •34. Понятие жизненного цикла ис (жц ис). Спиральная модель жц ис.
- •35. Анализ требований. Функциональные и нефункциональные требования.
- •36 Требования и прецеденты. Формат описания прецедента. Структура прецедента.
- •37 Требования и прецеденты. Взаимосвязь прецедентов.
- •38 Модель предметной области. Концептуальные классы. Ассоциации и атрибуты.
- •39 Язык uml. Способы использования. Model Driven Architecture. Executable uml. ДиаграммыUml.
- •40 Диаграмма прецедентов.
- •41. Диаграмма классов. Обозначение классов. Отношение ассоциации.
- •42.Диаграмма классов. Обозначение интерфейсов. Отношение обобщения и зависимости.
- •43.Диаграмма объектов.
- •44.Диаграмма пакетов.
- •45.Диаграмма состояний.
- •46.Диаграмма развертывания.
- •49.Диаграмма коммуникации.
- •50.Паттерн: понятие, структура, классификация.
- •1) Понятие паттерна
- •2) Структура и Классификация паттернов
- •51. Проектирование на основе обязанностей. Принципы grasp.
- •52.Grasp: принцип Low Coupling.
- •53.Grasp: принцип High Cohesion.
- •54.Grasp: принцип Information Expert.
- •55. Grasp: принцип Creator.
- •56.Grasp: принцип Pure Fabrication.
- •57. Grasp: принцип Indirection.
- •59.Grasp: принцип Protected Variations.
- •60.Grasp: принцип Controller.
16. C#. Виртуальные и невиртуальные методы. Статические методы.
Виртуальные методы и их переопределение
Виртуальным называется метод, объявляемый с помощью ключевого сова
v i r t u a l в базовом классе и переопределяемый в одном или нескольких производных классах. Таким образом, каждый производный класс может иметь собственную версию виртуального метода. Виртуальные методы представляют интерес с такой позиции: что произойдет, если виртуальный метод будет вызван посредством ссылки на базовый класс. Какую именно версию метода нужно вызвать, С# определяет по типу объекта, на который указывает эта ссылка, причем решение принимается динамически, во время выполнения программы. Следовательно, если имеются ссылки на различные объекты, будут выполняться различные версии виртуального метода. Другими словами, именно тип объекта, на который указывает ссылка (а не тип ссылки) определяет, какая версия виртуального метода будет выполнена. Таким образом, если базовый класс содержит виртуальный метод и из этого класса выведены производные классы, то при наличии ссылки на различные типы объектов (посредством ссылки на базовый класс) будут выполняться различные версии этого виртуального метода.
Чтобы объявить метод в базовом классе виртуальным, его объявление необходимо предварить ключевым словом v i r t u a l . При переопределении виртуального метода в производном классе используется модификатор override. Итак, процесс переопределения виртуального метода в производном классе иногда называется замещением метода (method overriding). При переопределении метода сигнатуры типа у виртуального и метода-заменителя должны совпадать. Кроме того, виртуальный метод нельзя определять как статический (с использованием слова s t a t i c ) или абстрактный (с использованием слова a b s t r a c t , о котором пойдет речь ниже в этой главе).
Переопределение виртуального метода формирует базу для одной из самых мощных концепций С#: динамической диспетчеризации методов. Динамическая диспетчеризация методов — это механизм вызова переопределенного метода во время выполнения программы, а не в период компиляции. Именно благодаря механизму диспетчеризации методов в С# реализуется динамический полиморфизм.
Рассмотрим пример, который иллюстрирует виртуальные методы и их переопреде-
ление.
// Демонстрация виртуального метода.
using System; i
class Base {
// Создаем виртуальный метод в базовом классе,
public virtual void who () {
Console.WriteLine("Метод who() в классе Base.");
class Derivedl : Base {
// Переопределяем метод who() в производном классе,
public override void who() {
Console.WriteLine("Метод who() в классе Derivedl")
class Derived2 : Base {
// Снова переопределяем метод who()
// в другом производном классе,
public override void who() {
Console.WriteLine("Метод who() в классе Derived2
class OverrideDemo {
public static void Main() {
Base baseOb = new Base();
Derivedl dObl = new Derivedl()
Derived2 dOb2 = new Derived2()
Base baseRef; // Ссылка на базовый класс.
baseRef = baseOb;
baseRef.who();
baseRef = dObl; ч
baseRef.who() ;
baseRef = dOb2;
baseRef.who() ;
Вот результаты выполнения этой программы: IМетод who() в классе Base.
Метод who() в классе Derivedl
Метод who() в классе Derived2
В программе создается базовый класс Base и два производных класса — Derivedl
и Derived2. В классе Base объявляется метод с именем who (), а производные классы
его переопределяют. В методе Main() объявляются объекты типа Base, Derivedl и
Derived2, а также ссылка baseRef типа Base. Затем программа поочередно присваи-
вает ссылку на объект каждого типа ссылке baseRef и использует эту ссылку для вы-
зова метода who (). Как показывают результаты выполнения этой программы, нужная
для выполнения версия определяется типом объекта, адресуемого в момент вызова, а
не "классовым" типом ссылки baseRef.
Виртуальный метод переопределять необязательно. Если производный класс не
предоставляет собственную версию виртуального метода, используется версия, опре-
деленная в базовом классе. Вот пример:
/* Если виртуальный метод не переопределен
в производном классе, используется метод
базового класса. Л/
using System;
class Base {
// Создаем виртуальный метод в базовом классе,
public virtual void who() {
Console.WriteLine("Метод who() в классе Base");
class Derivedl : Base {
// Переопределяем метод who() в производном классе,
public override void who() {
Console.WriteLine("Метод who() в классе Derivedl");
class Derived2 : Base {
// Этот класс не переопределяет метод who().
}
class NoOverrideDemo {
public static void Main() {
Base baseOb = new Base();
Derivedl dObl = new Derivedl();
Derived2 dOb2 = new Derived2();
Base baseRef; // Ссылка на базовый класс.
baseRef = baseOb;
baseRef.who(); ,
baseRef = dObl;
baseRef.who();
baseRef = dOb2;
baseRef.who(); // Вызывает метод who() класса Base.
Вот результаты выполнения этой программы:
Метод who() в классе Base
Метод who() в классе Derivedl
Метод who() в классе Base
Здесь класс Derived2 не переопределяет метод who (). Поэтому при вызове метода
who () для объекта класса Derived2 выполняется метод who (), определенный в клас-
се Base.
Если производный класс не переопределяет виртуальный метод в случае много-
уровневой иерархии, то будет выполнен первый переопределенный метод, который
обнаружится при просмотре иерархической лестницы в направлении снизу вверх.
Рассмотрим пример.
/* Если производный класс не переопределяет виртуальный
метод в случае многоуровневой иерархии, будет выполнен
первый переопределенный метод, который обнаружится
при просмотре иерархической лестницы в направлении
снизу вверх. */
using System;
class Base {
// Создаем виртуальный метод в базовом классе,
public virtual void who() {
Console.WriteLine("Метод who() в классе Base");
class Derivedl : Base {
// Переопределяем метод who() в производном классе,
public override void who() {
Console.WriteLine("Метод who() в классе Derivedl");
class Derived2 : Derivedl {
// Этот класс не переопределяет метод who().
}
class Derived3 : Derived2 {
// Этот класс также не переопределяет метод who().
}
class No0verrideDemo2 {
public static void Main{) {
Derived3 dOb = new Derived3();
Base baseRef; // Ссылка на базовый класс.
baseRef = dOb;
baseRef.who(); // Вызывает метод who()
// из класса Derivedl.
Результаты выполнения этой программы таковы:
I Метод who() в классе Derivedl
Здесь класс Derived3 наследует класс Derived2, который наследует класс
Derivedl, который в свою очередь наследует класс Base. Как подтверждают результа-
ты выполнения этой программы, поскольку метод who () не переопределяется ни в
классе Derived3, ни в классе Derived2, но переопределяется в классе Derivedl, то
именно эта версия метода who () (из класса Derivedl) и выполняется, так как она
является первой обнаруженной в иерархии классов.
Еще одно замечание. Свойства также можно модифицировать с помощью ключе-
вого слова v i r t u a l , а затем переопределять с помощью ключевого слова override.
Зачем переопределять методы
Переопределение методов позволяет С# поддерживать динамический полимор-
физм. Без полиморфизма объектно-ориентированное программирование невозможно,
поскольку он позволяет исходному классу определять общие методы, которыми будут
пользоваться все производные классы, и в которых $ри этом можно будет задать соб-
ственную реализацию некоторых или всех этих методов. Переопределенные методы
представляют собой еще один способ реализации в С# аспекта полиморфизма, кото-
рый можно выразить как "один интерфейс — много методов".
Ключ (вернее, его первый "поворот") к успешному применению полиморфизма
лежит в понимании того, что базовые и производные классы образуют иерархию, ко-
торая развивается в сторону более узкой специализации. При корректном использо-
вании базовый класс предоставляет производному классу все элементы "пригодными
к употреблению", т.е. для прямого их использования. Кроме того, он определяет ме-
тоды, которые производный класс должен реализовать самостоятельно. Это делает оп-
ределение производными классами собственных методов более гибким, по-прежнему
оставляя в силе требование согласующегося интерфейса. Таким образом, сочетая на-
следование с возможностью переопределения (замещения) методов, в базовом классе
можно определить общую форму методов, которые будут использованы производны-
ми классами.
Применение виртуальных методов
Чтобы лучше почувствовать силу виртуальных методов, применим их к классу
TwoDShape. В предыдущих примерах каждый класс, выведенный из класса
TwoDShape, определяет метод с именем area (). Это наводит нас на мысль о том, не
лучше ли сделать метод вычисления площади фигуры агеа() виртуальным в классе
TwoDShape, получив возможность переопределить его в производных классах таким
образом, чтобы он вычислял площадь согласно типу конкретной геометрической фи-
гуры, которую инкапсулирует класс.
