Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Виртуальные методы, свойства и индексаторы

Поиск

 

Полиморфизм предоставляет подклассу способ определения собственной версии метода, определенного в его базовом классе, с использованием процесса, который называется переопределением метода (method overriding). Чтобы пересмотреть текущий дизайн, нужно понять значение ключевых слов virtual и override.

О полиморфизме часто говорят как о третьем базовом элементе объектно-ориентированного программирования, после инкапсуляции и наследования. Полиформизм — это греческое слово, означающее "наличие многих форм". Это понятие имеет два различающихся аспекта.

  1. Во время выполнения объекты производного класса могут рассматриваться как объекты базового класса в таких местах как параметры метода и коллекции массивов. При этом объявленный тип объекта больше не идентичен его типу времени выполнения.
  2. Базовые классы могут определять и реализовывать виртуальные методы, а производные классы могут переопределять их. Это означает, что они предоставляют свои собственные определение и реализацию. Во время выполнения, когда клиентский код вызывает метод, среда CLR ищет тип времени выполнения объекта и вызывает это переопределение виртуального метода. Таким образом, в исходном коде можно вызвать метод в базовом классе и вызвать выполнение метода с версией производного класса.

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

  1. Создание иерархии классов, в которой класс каждой конкретной формы производится от общего базового класса.
  2. Использование виртуального метода для вызова соответствующего метода в каком-либо производном классе одним вызовом метода базового класса.

 

Виртуальным называется такой метод, который объявляется как virtual в базовом классе. Виртуальный метод отличается тем, что он может быть переопределен в одном или нескольких производных классах. Следовательно, у каждого производного класса может быть свой вариант виртуального метода. Кроме того, виртуальные методы интересны тем, что именно происходит при их вызове по ссылке на базовый класс. В этом случае средствами языка С# определяется именно тот вариант виртуального метода, который следует вызывать, исходя из типа объекта, к которому происходит обращение по ссылке, причем это делается во время выполнения. Поэтому при ссылке на разные типы объектов выполняются разные варианты виртуального метода. Иными словами,вариант выполняемого виртуального метода выбирается по типу объекта, а не по типу ссылки на этот объект.

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

Метод объявляется как виртуальный в базовом классе с помощью ключевого слова virtual, указываемого перед его именем. Когда же виртуальный метод переопределяется в производном классе, то для этого используется модификатор override. А сам процесс повторного определения виртуального метода в производном классе называется переопределением метода. При переопределении имя, возвращаемый тип и сигнатура переопределяющего метода должны быть точно такими же, как и у того виртуального метода, который переопределяется. Кроме того, виртуальный метод не может быть объявлен как static или abstract.

Переопределение метода служит основанием для воплощения одного из самых эффективных в С# принципов: динамической диспетчеризации методов, которая представляет собой механизм разрешения вызова во время выполнения, а не компиляции. Значение динамической диспетчеризации методов состоит в том, что именно благодаря ей в С# реализуется динамический полиморфизм.

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

И ещё одно замечание: свойства также подлежат модификации ключевым словом virtual и переопределению ключевым словом override. Это же относится и к индексаторам.

Рассмотрим пример использования виртуальных методов, свойств и индексаторов:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

// Реализуем класс содержащий информацию о шрифтах

// и использующий виртуальные методы, свойства и индексаторы

 

// Базовый класс

class Font

{

string TypeFont;

short FontSize;

 

public Font()

{

TypeFont = "Arial";

FontSize = 12;

}

 

public Font(string TypeFont, short FontSize)

{

this.TypeFont = TypeFont;

this.FontSize = FontSize;

}

 

public string typeFont

{

get

{

return TypeFont;

}

set

{

TypeFont = value;

}

}

 

public short fontSize

{

get

{

return FontSize;

}

set

{

FontSize = value;

}

}

// Создаем виртуальный метод

public virtual string FontInfo(Font obj)

{

string s = "Информация о шрифте: \n------------------\n\n" +

"Тип шрифта: " + typeFont +

"\nРазмер шрифта: " + fontSize + "\n";

return s;

}

}

// Производный класс 1 уровня

class ColorFont: Font

{

byte Color;

 

public ColorFont(byte Color, string TypeFont, short FontSize)

: base(TypeFont, FontSize)

{

this.Color = Color;

}

// Переопределение для виртуального метода

public override string FontInfo(Font obj)

{

// Используется ссылка на метод определенный в базовом классе Font

return base.FontInfo(obj) + "Цвет шрифта: " + Color + "\n";

}

// Создадим виртуальное свойство

public virtual byte color

{

set

{

Color = value;

}

get

{

return Color;

}

}

}

 

// Производный класс 2 уровня

class GradientColorFont: ColorFont

{

char TypeGradient;

 

public GradientColorFont(char TypeGradient, byte Color, string TypeFont, short FontSize):

base(Color, TypeFont, FontSize)

{

this.TypeGradient = TypeGradient;

}

// Опять переопределяем виртуальный метод

public override string FontInfo(Font obj)

{

// Используется ссылка на метод определенный в производном классе FontColor

return base.FontInfo(obj) + "Тип градиента: " + TypeGradient + "\n\n";

}

// Переопределим виртуальное свойство

public override byte color

{

get

{

return base.color;

}

set

{

if (value < 10)

base.color = 0;

else

base.color = (byte)(value - 0x0A);

}

}

}

// Еще один производный класс 1 уровня

class FontStyle: Font

{

string style;

 

public FontStyle(string style, string TypeFont, short FontSize): base (TypeFont, FontSize)

{

this.style = style;

}

// Данный метод не переопределяет виртуальный метод

// поэтому при вызове метода FontInfo()

// вызывыется метод созданный в базовом классе

}

 

class Program

{

static void Main()

{

ColorFont font1 = new ColorFont(Color: 0xCF, TypeFont: "MS Trebuchet", FontSize: 16);

Console.WriteLine(font1.FontInfo(font1));

GradientColorFont font2 = new GradientColorFont(Color: 0xFF, TypeFont: "Times New Roman", FontSize: 10, TypeGradient: 'R');

Console.WriteLine(font2.FontInfo(font2));

font2.color = 0x2F;

Console.ForegroundColor = ConsoleColor.Green;

Console.WriteLine("Видоизмененный цвет font2");

Console.ForegroundColor = ConsoleColor.Gray;

Console.WriteLine(font2.FontInfo(font2));

FontStyle font3 = new FontStyle(style: "oblique", TypeFont: "Calibri", FontSize: 16);

Console.WriteLine(font3.FontInfo(font3));

Console.WriteLine("Для продолжения нажмите любую клавишу...");

Console.ReadKey();

}

}

}

 

Рис. 1. 1. Результат работы кода выше

 

Рассмотрим данный пример более подробно. В базовом классе Font инкапсулируется виртуальный метод FontInfo, возвращающий информацию о шрифте. В производном классе FontColor данный метод переопределяется с помощью ключевого слова override, поэтому при создании экземпляра данного класса и вызова метода FontInfo в исходную информацию возвращается по первоначальных данных еще и цвет шрифта. Затем данный метод вновь переопределяется в классе GradientColorFont, унаследованном от класса FontColor. Обратите внимание, что здесь переопределяется не исходный метод базового класса Font, а уже переопределенный метод класса FontColor. В этом и заключается принцип динамического полиморфизма!

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

Абстрактные классы

Абстрактные классы

 

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

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

Абстрактный метод создается с помощью указываемого модификатора типа abstract. У абстрактного метода отсутствует тело, и поэтому он не реализуется в базовом классе. Это означает, что он должен быть переопределен в производном классе, поскольку его вариант из базового класса просто непригоден для использования. Нетрудно догадаться, что абстрактный метод автоматически становится виртуальным и не требует указания модификатора virtual. В действительности совместное использование модификаторов virtual и abstract считается ошибкой. Для определения абстрактного метода служит приведенная ниже общая форма:

 

abstract <тип> <имя>(<список параметров>);

 

Как видно, у абстрактного метода отсутствует тело. Модификатор abstract может применяться только в методах экземпляра, но не в статических методах (static).Абстрактными могут быть также индексаторы и свойства.

Класс, содержащий один или больше абстрактных методов, должен быть также объявлен как абстрактный, и для этого перед его объявлением class указывается модификатор abstract. А поскольку реализация абстрактного класса не определяется полностью, то у него не может быть объектов. Следовательно, попытка создать объект абстрактного класса с помощью оператора new приведет к ошибке во время компиляции!

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

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace ConsoleApplication1

{

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

 

namespace LC_Console

{

// Создаем абстакный класс

abstract class UserInfo

{

protected string Name;

protected byte Age;

 

public UserInfo(string Name, byte Age)

{

this.Name = Name;

this.Age = Age;

}

// Абстрактный метод

public abstract string ui();

}

 

class UserFamily: UserInfo

{

string Family;

 

public UserFamily(string Family, string Name, byte Age): base (Name, Age)

{

this.Family = Family;

}

// Переопределяем метод ui

public override string ui()

{

return Family + " " + Name + " " + Age;

}

}

 

class Program

{

static void Main(string[] args)

{

UserFamily user1 = new UserFamily("Vladimir", "Medvedev", 55);

Console.WriteLine(user1.ui());

Console.WriteLine("Для продолжения нажмите любую клавишу...");

Console.ReadKey();

}

}

}

/* Выведет:

* Vladmimir Medvedev 55

* Для продолжения нажмите любую клавишу...

*/

 

В данном примере создается абстрактный класс UserInfo в котором инкапсулируется абстрактный метод ui(), который, в свою очередь, переопределяется в классе UserFamily.

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



Поделиться:


Последнее изменение этой страницы: 2016-08-15; просмотров: 735; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.12.74.189 (0.009 с.)