...
// Здесь вызывается метод 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
определен новый метод:
static void GivePromotion(Employee emp)
{
// Повысить зарплату...
// Предоставить место на парковке компании...
Console.WriteLine("{0} was promoted!", emp.Name);
}
Из-за того, что данный метод принимает единственный параметр типа
Employee
, в сущности, ему можно передавать объект любого унаследованного от
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);
GivePromotion(moonUnit);
// PtSalesPerson "является" SalesPerson.
SalesPerson jill = new PtSalesPerson("Jill", 834, 3002, 100000,
"111-12-1119", 90);
GivePromotion(jill);
}
Предыдущий код компилируется благодаря неявному приведению от типа базового класса (
Employee
) к производному классу. Но что, если вы хотите также вызвать метод
GivePromotion()
с объектом
frank
(хранящимся в общей ссылке
System.Object
)? Если вы передадите объект
frank
методу
GivePromotion()
напрямую, то получите ошибку на этапе компиляции:
object frank = new Manager("Frank Zappa", 9, 3000, 40000, "111-11-1111", 5);
// Ошибка!
GivePromotion(frank);
Проблема в том, что вы пытаетесь передать переменную, которая объявлена как принадлежащая не к типу
Employee
, а к более общему типу
System.Object
. Учитывая, что в цепочке наследования он находится выше, чем
Employee
, компилятор не разрешит неявное приведение, стараясь сохранить ваш код насколько возможно безопасным в отношении типов.