Первым правилом преобразований для типов классов является то, что когда два класса связаны отношением подчиненности ("is-a"), всегда можно сохранить производный тип в ссылке базового класса. Формально его называют
Такой подход позволяет строить очень мощные программные конструкции. Предположим, например, что у нас есть класс TheMachine (машина), который поддерживает следующий статический метод, соответствующий увольнению работника.
public class TheMachine {
public static void FireThisPerson(Employee e) {
// Удалить из базы данных…
// Забрать у работника ключи и точилку…
}
}
Мы можем непосредственно передать этому методу любой производный класс класса Employee ввиду того, что эти классы связаны отношением подчиненности ("is-a").
// Сокращение штатов.
TheMaсhine.FireThisPerson(moonUnit);
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.
Распознавание типов
Статический метод TheMachine.FireThisPerson() строился так, чтобы он мог принимать любой тип, производный от Employee, но возникает один вопрос: как метод "узнает", какой именно производный тип передается методу. Кроме того, если поступивший параметр имеет тип Employee, то как получить доступ к специфическим членам типов SalesPerson и Manager?
Язык C# обеспечивает три способа определения того, что ссылка базового класса действительно указывает на производный тип: явное приведение типа (рассмотренное выше), ключевое слово is и ключевое слово as. Ключевое слово is возвращает логическое значение, указывающее на совместимость ссылки базового класса с данным производным типом. Рассмотрим следующий обновленный метод FireThisPerson().
public class TheMachine {
public static void FireThisPerson(Employee e) {
if (e is SalesPerson) {
Console.WriteLine("Имя уволенного продавца: {0}", e.GetFullName());