Язык программирования C#9 и платформа .NET5
...// Здесь вызывается метод Draw(), определенный в классе ThreeDCircle.ThreeDCircle o = new ThreeDCircle();o.Draw();// Здесь вызывается метод Draw(), определенный в родительском классе!((Circle)o).Draw();Console.ReadLine();Правила приведения для базовых и производных классов
Теперь, когда вы умеете строить семейства взаимосвязанных типов классов, нужно изучить правила, которым подчиняются операции приведения классов. Давайте возвратимся к иерархии классов для сотрудников, созданной ранее в главе, и добавим несколько новых методов в класс
(если вы прорабатываете примеры, тогда откройте проектProgramв Visual Studio). Как описано в последнем разделе настоящей главы, изначальным базовым классом в системе являетсяEmployees. По указанной причине любой класс "является"System.Objectи может трактоваться как таковой. Таким образом, внутри переменной типаObjectдопускается хранить экземпляр любого типа:objectstatic void CastingExamples(){// Manager "является" System.Object, поэтому в переменной// типа object можно сохранять ссылку на Manager.object frank = new Manager("Frank Zappa", 9, 3000, 40000, "111-11-1111", 5);}В проекте
классыEmployees,ManagerиSalesPersonрасширяют классPtSalesPerson, а потому допустимая ссылка на базовый класс может хранить любой из объектов указанных классов. Следовательно, приведенный далее код также законен:Employeestatic 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);}Первое правило приведения между типами классов гласит, что когда два класса связаны отношением "является", то всегда можно безопасно сохранить объект производного типа в ссылке базового класса. Формально это называется неявным приведением, поскольку оно "просто работает" в соответствии с законами наследования. В результате появляется возможность строить некоторые мощные программные конструкции. Например, предположим, что в текущем классе
определен новый метод:Programstatic void GivePromotion(Employee emp){// Повысить зарплату...// Предоставить место на парковке компании...Console.WriteLine("{0} was promoted!", emp.Name);}Из-за того, что данный метод принимает единственный параметр типа
, в сущности, ему можно передавать объект любого унаследованного отEmployeeкласса, учитывая наличие отношения "является":Employeestatic 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