Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

В целях иллюстрации предположим, что вы получили от коллеги на доработку класс по имени

ThreeDCircle
, в котором определен метод
Draw
, не принимающий аргументов:

class ThreeDCircle

{

public void Draw

{

Console.WriteLine("Drawing a 3D Circle");

}

}

Вы полагаете, что

ThreeDCircle
"является"
Circle
, поэтому решаете унаследовать его от своего существующего типа
Circle
:

class ThreeDCircle : Circle

{

public void Draw

{

Console.WriteLine("Drawing a 3D Circle");

}

}

После

перекомпиляции вы обнаружите следующее предупреждение:

'ThreeDCircle.Draw' hides inherited member 'Circle.Draw'. To make

the current member
override that implementation, add the override keyword.

Otherwise add the new keyword.

'Shapes.ThreeDCircle.Draw
скрывает унаследованный член
Shapes.Circle.Draw
.

Чтобы текущий член переопределял эту реализацию, добавьте ключевое слово

override
.

В противном случае добавьте ключевое слово

new
.'

Дело в том, что у вас есть производный класс (

ThreeDCircle
), который содержит метод, идентичный унаследованному методу. Решить проблему можно несколькими способами. Вы могли бы просто модифицировать версию метода
Draw
в дочернем классе, добавив ключевое слово
override
(как предлагает компилятор). При таком подходе у типа
ThreeDCircle
появляется возможность расширять стандартное поведение родительского типа, как и требовалось. Однако если у вас нет доступа к файлу кода с определением базового класса (частый случай, когда приходится работать с множеством библиотек от сторонних поставщиков), тогда нет и возможности изменить метод
Draw
, превратив его в виртуальный член.

В качестве альтернативы вы можете добавить ключевое слово

new
к определению проблемного члена
Draw
своего производного типа (
ThreeDCircle
). Поступая так, вы явно утверждаете, что реализация производного типа намеренно спроектирована для фактического игнорирования версии члена из родительского типа (в реальности это может оказаться полезным, если внешнее программное обеспечение каким-то образом конфликтует с вашим программным обеспечением).

// Этот класс расширяет Circle и скрывает унаследованный метод Draw.

class ThreeDCircle : Circle

{

// Скрыть любую реализацию Draw, находящуюся выше в иерархии.

public new void Draw

{

Console.WriteLine("Drawing a 3D Circle");

}

}

Вы можете также применить ключевое слово

new
к любому члену типа, который унаследован от базового класса (полю, константе, статическому члену или свойству). Продолжая пример, предположим, что в классе
ThreeDCircle
необходимо скрыть унаследованное свойство
PetName
:

class ThreeDCircle : Circle

{

// Скрыть свойство PetName, определенное выше в иерархии.

public new string PetName { get; set; }

//
Скрыть любую реализацию Draw, находящуюся выше в иерархии.

public new void Draw

{

Console.WriteLine("Drawing a 3D Circle");

}

}

Наконец, имейте в виду, что вы по-прежнему можете обратиться к реализации скрытого члена из базового класса, используя явное приведение, как описано в следующем разделе. Вот пример:

...

// Здесь вызывается метод Draw, определенный в классе ThreeDCircle.

ThreeDCircle o = new ThreeDCircle;

o.Draw;

// Здесь вызывается метод Draw, определенный в родительском классе!

((Circle)o).Draw;

Console.ReadLine;

Правила приведения для базовых и производных классов

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

Program
(если вы прорабатываете примеры, тогда откройте проект
Employees
в Visual Studio). Как описано в последнем разделе настоящей главы, изначальным базовым классом в системе является
System.Object
. По указанной причине любой класс "является"
Object
и может трактоваться как таковой. Таким образом, внутри переменной типа
object
допускается хранить экземпляр любого типа:

static void CastingExamples

{

// Manager "является" System.Object, поэтому в переменной

// типа object можно сохранять ссылку на Manager.

object frank = new Manager("Frank Zappa", 9, 3000, 40000, "111-11-1111", 5);

}

В проекте

Employees
классы
Manager
,
SalesPerson
и
PtSalesPerson
расширяют класс
Employee
, а потому допустимая ссылка на базовый класс может хранить любой из объектов указанных классов. Следовательно, приведенный далее код также законен:

static void CastingExamples

{

// Manager "является" System.Object, поэтому в переменной

// типа object можно сохранять ссылку на Manager.

object frank = new Manager("Frank Zappa", 9, 3000, 40000, "111-11-1111", 5);

// Manager тоже "является" Employee.

Employee moonUnit = new Manager("MoonUnit Zappa", 2, 3001, 20000,

"101-11-1321", 1);

// PtSalesPerson "является" SalesPerson.

SalesPerson jill = new PtSalesPerson("Jill", 834, 3002, 100000,

"111-12-1119", 90);

}

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

Program
определен новый метод:

Поделиться с друзьями: