Из-за того, что данный метод принимает единственный параметр типа
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
, компилятор не разрешит неявное приведение, стараясь сохранить ваш код насколько возможно безопасным в отношении типов.
Несмотря на то что сами вы можете выяснить, что ссылка
object
указывает в памяти на объект совместимого с
Employee
класса, компилятор сделать подобное не в состоянии, поскольку это не будет известно вплоть до времени выполнения. Чтобы удовлетворить компилятор, понадобится применить явное приведение, которое и является вторым правилом: в таких случаях вы можете явно приводить "вниз", используя операцию приведения С#. Базовый шаблон, которому нужно следовать при выполнении явного приведения, выглядит так:
Имейте в виду, что явное приведение оценивается во время выполнения, а не на этапе компиляции. Ради иллюстрации
предположим, что проект
Employees
содержит копию класса
Hexagon
, созданного ранее в главе. Для простоты вы можете добавить в текущий проект такой класс:
class Hexagon
{
public void Draw
{
Console.WriteLine("Drawing a hexagon!");
}
}
Хотя приведение объекта сотрудника к объекту фигуры абсолютно лишено смысла, код вроде показанного ниже скомпилируется без ошибок:
// Привести объект frank к типу Hexagon невозможно,
// но этот код нормально скомпилируется!
object frank = new Manager;
Hexagon hex = (Hexagon)frank;
Тем не менее, вы получите ошибку времени выполнения, или более формально — исключение времени выполнения. В главе 7 будут рассматриваться подробности структурированной обработки исключений, а пока полезно отметить, что при явном приведении можно перехватывать возможные ошибки с применением ключевых слов
try
и
catch
:
// Перехват возможной ошибки приведения.
object frank = new Manager;
Hexagon hex;
try
{
hex = (Hexagon)frank;
}
catch (InvalidCastException ex)
{
Console.WriteLine(ex.Message);
}
Очевидно, что показанный пример надуман; в такой ситуации вас никогда не будет беспокоить приведение между указанными типами. Однако предположим, что есть массив элементов
System.Object
, среди которых лишь малая толика содержит объекты, совместимые с
Employee
. В этом случае первым делом желательно определить, совместим ли элемент массива с типом
Employee
, и если да, то лишь тогда выполнить приведение.
Для быстрого определения совместимости одного типа с другим во время выполнения в C# предусмотрено ключевое слово
as
. С помощью ключевого слова
as
можно определить совместимость, проверив возвращаемое значение на предмет
null
. Взгляните на следующий код:
// Использование ключевого слова as для проверки совместимости.
object[] things = new object[4];
things[0] = new Hexagon;
things[1] = false;
things[2] = new Manager;
things[3] = "Last thing";
foreach (object item in things)
{
Hexagon h = item as Hexagon;
if (h == null)
{
Console.WriteLine("Item is not a hexagon"); // item - не Hexagon