Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
}
}
После перекомпиляции вы обнаружите следующее предупреждение:
'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 определен новый метод:
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. Учитывая, что