Правила приведения типов в C#
Правила приведения типов в C#
Пришло время изучить правила выполнения операций приведения типов в C#. Вспомните иерархию Employees и тот факт, что наивысшим классом в системе является System.Object. Поэтому все в вашей программе является объектами и может рассматриваться, как объекты. С учетом этого вполне допустимо сохранять экземпляры любого типа в объектных переменных.
// Manager – это System.Object.
object frank = new Manager("Frank Zappa", 9, 40000, "111-11-1111", 5);
В системе Employees типы Manager, Salesperson и PTSalesPerson расширяют Employee, поэтому можно запомнить любой из этих объектов в подходящей ссылке базового класса. Так что допустимыми будут и следующие операторы.
// Manager - это Employee.
Employee moonUnit = new Manager("MoonUnit Zappa", 2, 20000, "101-11-1321", 1);
// PTSalesPerson - это Salesperson.
Salesperson jill = new PTSalesPerson("Jill", 834, 100000, "111-12-1119", 90);
Первым правилом преобразований для типов классов является то, что когда два класса связаны отношением подчиненности ("is-a"), всегда можно сохранить производный тип в ссылке базового класса. Формально его называют неявным (кон-текстуальным) приведением типов, поскольку оно "работает" только в рамках законов наследования.
Такой подход позволяет строить очень мощные программные конструкции. Предположим, например, что у нас есть класс TheMachine (машина), который поддерживает следующий статический метод, соответствующий увольнению работника.
public class TheMachine {
public static void FireThisPerson(Employee e) {
// Удалить из базы данных…
// Забрать у работника ключи и точилку…
}
}
Мы можем непосредственно передать этому методу любой производный класс класса Employee ввиду того, что эти классы связаны отношением подчиненности ("is-a").
// Сокращение штатов.
TheMaсhine.FireThisPerson(moonUnit); // "moonUnit" - это Employee.
TheMachine.FireThisFerson(jill); //"jill" - это SalesPerson.
В дальнейшем программный код использует в производном типе неявное преобразование из базового класса (Employee). Но что делать, если вы хотите уволить служащего по имени Frank Zарра (информация о котором в настоящий момент хранится в ссылке System.Object общего вида)? Если передать объект frank непосредственно в TheMaсhine.FireThisPerson() так, как показано ниже:
// Manager - это object, но… .
object frank = new Manager("Frank Zappa", 9, 40000, "111-11-1111", 5);
…
TheMachine.FireThisPerson(frank); // Ошибка!
то вы получите ошибку компиляции. Причина в том, что нельзя автоматически интерпретировать System.Object, как объект, являющийся производным непосредственно от Employee, поскольку Object не является Employee. Но вы можете заметить, что объектная ссылка указывает на объект, связанный с Employee. Поэтому компилятор будет "удовлетворен", если вы используете явное приведение типов.
В C# явное приведение типов указывается с помощью скобок, размещаемых вокруг имени типа, к которому вы хотите прийти, с последующим указанием объекта, который вы пытаетесь использовать в качестве исходного. Например:
// Приведение общего типа System.Object
// к строго типизованному Manager.
Manager mgr = (Manager)frank;
Console.WriteLine("Опционы Фрэнка: {0}", mgr.NumbOpts);
Если не объявлять специальную переменную для "целевого типа", то можно получить более компактный программный код.
// "Внутристрочное" явное приведение типов.
Console.WriteLine("Опционы Фрэнка: {0}", ((Manager)frank).NumbOpts);
Проблему, связанную с передачей ссылки System.Object методу FireThisPerson(), можно решить так, как показано ниже.
// Явное приведение типа System.Object к Employee.
TheMachine.FireThisPerson((Employee)frank);
Замечание. Попытка приведения объекта к несовместимому типу порождает в среде выполнения соответствующее исключение. Возможности структурированной обработки исключений будут рассмотрены в главе 6.