Главная » C# » Дополнительные сведения о наследовании и приведении типов в Visual C# (Sharp)

0

В этой главе были представлены интерфейсы и компоненты, а также было продоено рассмотрение наследования. В этом разделе мы обсудим дополнительные сведения о наследовании и преобразовании типов.

Наследование

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

ПРИМЕЧАНИЕ

Во всех примерах для демонстрации наследования применяются методы, но такие же способы наследования можно применять с помощью свойств.

i Сценарий 7.1. Перегрузка функциональности базового класса

class Base {

public void Method() { Console.WriteLine("Base.Method");

}

}

class Derived : Base { public new void Method() {

Console.WriteLine("Derived.Method");

}

}

class Test {

public static void Run() {

Derived derivedCls = new Derived(); Base baseCls = derivedCls;

// Вызываем метод Derived.Method derivedCls.Method();

// Вызываем метод Base.Method baseCls.Method();

}

}

В данном сценарии используется ключевое слово new, чтобы указать перегрузку метода. Перегрузка метода означает изменение его  функциональности  в  произвоом  классе.

Вызов конкретного метода в наследовании зависит от типа объекта, в котором вызывается метод. Таким образом, для переменной типа Base вызывается метод Base.Method(), а ДЛЯ переменного типа Derived — метод Derived.Method().

Сценарий 7.2. Перегрузка функциональности базового класса

class Base {

public virtual void Method() { Console.WriteLinef"Base.Method");

}

}

class Derived : Base {

public override void Method() { Console.WriteLine("Derived.Method");

}

}

class Test {

public static void Run() {

Derived derivedCls = new Derivedf); Base baseCls = derivedCls;

// Вызываем метод Derived.Method derivedCls.Method();

// Вызываем метод Derived.Method baseCls.Method();

}

}

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

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

Метод виртуального базового класса можно также объявить с помощью ключевого слова abstract. Разница между virtual и abstract состоит в том, что virtual имеет реализацию метода или свойства, в то время как abstract такой реализации не имеет.

Сценарий 7.3. Реализация интерфейса

interface Ilnterface { void Method();

}

class Implementation : Ilnterface { public void Methodf) {

Console.WriteLine("Implementation.Method");

}

}

class Test {

public static void Run()   {

Implementation implementation = new Implementation(); Ilnterface inst = implementation;

// Вызываем метод Implementation.Method implementation.Method();

// Вызываем метод Implementation.Method inst.Methodf);

}

}

Рассматриваемы е  ключевы е  слов а  н е  используются .

Поведени е  класса ,  реализующег о   интерфейс ,  подобн о  производном у  классу ,   реали – зующем у  абстрактны й   метод .

Независим о   о т  того ,   обращаемс я   л и   м ы   к   экземпляр у   интерфейс а   ил и   к   самом у классу ,   вызываетс я   мето д  Implementation.Methodf).

Сценарий 7.4. Реализация двух интерфейсов с одинаковым именем метода или свойства

interface Ilnterfacel { void Method();

}

interface Ilnterface2 { void Methodf);

}

class Implementation : Ilnterfacel, Ilnterface2 { void Ilnterfacel.Methodf) {

Console.WriteLine("Implementation.Ilnterfacel.Method");

}

void IInterface2.Methodf) { Console.WriteLine("Implementation.Ilnterfacel.Method");

}

}

class Test {

public static void Run() {

Implementation implementation = new Implementation(); Ilnterfacel instl = implementation;

Ilnterfacel inst2 = implementation;

// Метод implementation.Ilnterfacel.Method()нельзя вызывать.

// Вызываем метод Implementation.Ilnterfacel.Method instl.Method();

// Вызываем Implementation.Ilnterface2.Method inst2.Method();

}

}

В данном методе применяется специальная нотация для реализации определенного метод а интерфейс а (например ,  Ilnterfacel .Method() И IInterface2 .Method() ).

Специальная нотация состоит в указании идентификатора интерфейса перед имем  метода. Перед методом  нельзя  указывать идентификатор  области  видимости, т. к. метод должен быть частным для данного класса.

Методы нельзя вызывать как стандартные методы класса. Это можно делать, толо выполнив приведение типа к соответствующему интерфейсу. Дополнительные подробности о приведении типов обсуждаются в следующем разделе.

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

Сценарий 7.5. Реализация интерфейса в производном классе

interface Ilnterface { void Methodf);

}

class Baselmplementation { public void MethodO {

Console.WriteLinef"Implementation.Method");

} .

}

class implementationDerived : Baselmplementation, Ilnterface {

}

class Test {

public static void Run О {

ImplementationDerived implementation = new ImplementationDerived!);

Ilnterface inst = implementation;

// Вызываем метод Implementation.Method implementation.Method();

I! Вызываем Implementation.Method inst.Methodf);

}

}

Рассматриваемы е  ключевы е  слов а  н е  используются .

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

Есл и н е  соблюдат ь  осторожност ь  с  подмено й  и  перегрузко й   методов ,  т о  это т  спо – со б  може т  вызват ь  странны е  побочны е  эффекты .

Сценарий 7,6. Реализация интерфейса, позволяющего подмену

interface Ilnterface { void Method();

}

class Implementation : Ilnterface { public virtual void Method() {

Console.WriteLine("Implementation.Method");

}

}

class implementationDerived : Implementation { public override void Method() {

Console.WriteLine("ImplementationDerived.Method");

}

}

class Test {

public static void Run() {

ImplementationDerived implementation = new ImplementationDerived(); Ilnterface inst = implementation;

// Вызываем метод ImplementationDerived.Method implementation.Method();

// Вызываем метод ImplementationDerived.Method inst.Method();

}

}

В  данно м  сценари и  применяютс я  ключевы е  слов а  virtual и  override.

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

Эт о распространенна я проблема , и поведени е перегрузк и приводи т большинств о начинающи х  программисто в  С #  в  замешательство .

Сценарий 7.7. Дерево наследования с подменой и перегрузкой

class Base {

public virtual void Method() { Console.WriteLine("Base.Method");

}

}

class Derivedl : Base {

public override void Method() { Console.WriteLine("Derivedl.Method");

}

}

class Derived2 : Derivedl {

public new virtual void MethodO { Console.WriteLine("Derived2.Method");

}

}

class Derived3 : Derived2 {

public new virtual void MethodO { Console.WriteLine("Derived3.Method");

}

}

class Test {

public static void Run() {

Derived3 derivedCls = new Derived3(); Base baseCls = derivedCls;

Derived2 derived2cls = derivedCls;

// Вызываем метод Derived3.Method derivedCls.Method();

// Вызываем метод Derived.Method baseCls.MethodO ;

// Вызываем метод Derived3.Method derived2cls.Methodf);

}

}

В этом сценарии применяются ключевые слова virtual, override и new. -

Данная   иерархия   наследования   вызывает  затруднения  у  большинства  программтов  С#  и требует  внимательного  отношения.

Представленная иерархия наследования указывает, что метод Derivedi .Method() подменяет метод Base.Method(). Метод Derived2.Method() перегружает метод Derivedi. Method (), при этом устанавливая  новый подменяющий базовый метод. Метод Derived3 .Method ()  подменяет  метод  Derived2 .Method (), НО не  метод Base .Method (), что очень важно иметь в виду.

При  работе  со  сложной   иерархией   наследования,   подобной   показанной   в  сцении 7.7,  важно  начинать с базового  класса и  продвигаться  вверх по иерархии.

Источник: Гросс  К. С# 2008:  Пер. с англ. — СПб.:  БХВ-Петербург, 2009. — 576 е.:  ил. — (Самоучитель)

По теме:

  • Комментарии