Сейчас мы рассмотрим один из самых важных аспектов типов — приведение. Допустим, существует базовый класс под названием
Employee
и производный класс
ContractEmployee.
Следующий код будет работать, поскольку всегда подразумевается восходящее приведение (upcast) производного класса к его базовому классу:
class Employee { }
class ContractEmployee : Employee { }
class CastExamplel
{
public static void Main () {
Employee e = new ContractEmployeeQ; } }
А вот такой код недопустим, так как компилятор не предоставляет неявное нисходящее приведение (downcast).
class Employee { }
class ContractEmployee : Employee { }
class CastExample2 {
public static void Main ()
{
ContractEmployee ce = new EmployeeQ; // He будет
// компилироваться.
} }
Причина различного поведения этих фрагментов кода, описанная в главе 1, связана с понятием заменяемости (substitutability). Правила заменяемости гласят: производный класс может быть использован вместо своего базового класса. Поэтому объект типа
ContractEmployee
всегда можно использовать вместо объекта
Employee.
Потому и компилируется код первого примера.
Однако вы не сможете выполнить нисходящее приведение объекта типа
Employee
к объекту типа
ContractEmployee,
поскольку нет гарантии, что этот объект поддерживает интерфейс, определенный классом
ContractEmployee.
Поэтому в случае нисходящего приведения используется явное приведение:
class Employee { }
class ContractEmployee : Employee { }
class CastExampleS {
public static void Main ()
{
// Нисходящее приведение не сработает.
ContractEmployee ce = (ContractEmployee)new Employee(); } }
А давайте обманем CTS путем явного приведения базового класса к производному:
class Employee { }
class ContractEmployee : Employee { }
class CastExample4 {
public static void Main ()
<
Employee e = new EmployeeO; ContractEmployee с = (ContractEmployee)e; } }
Эта программа компилируется, но генерирует исключение периода выполнения. Здесь важны два момента. Во-первых, ошибка периода компиляции не возникает, так как
е
на самом деле может быть объектом
ContractEmployee,
приведенным к базовому классу. Поэтому истинная природа объекта, приведенного к базовому классу, не может быть распознана до периода выполнения. Во-вторых, CLR определяет типы объектов в период выполнения. Распознав неверное приведение, CLR генерирует исключение
System.InvalidCastException.
Есть еще один способ приведения объектов — ключевое слово «5. Преимущество использования
as
вместо собственно приведения в том, что в случае неверного приведения вам не придется беспокоиться о возникающем исключении. Вместо этого вы получите
null,
например:
using System;
class Employee { >
class ContractEmployee : Employee { }
class CastExampleS {
public static void Main ()
{
Employee e = new EmployeeO;
Console.WriteLine("e = {0}",
e == null ? "null" : e.ToStringO);
ContractEmployee с = e as ContractEmployee; Console.WriteLine("c = {0}",
с == null ? "null" : e.ToStringO); > }
Запустив этот пример, вы увидите такой результат:
c:>CastExample5 e = Employee с = null
Способность сравнивать объект с
null
означает, что вы ничем не рискуете, используя пустые объекты. Фактически, если в приведенном выше примере вызвать метод
System.Object
для объекта
с,
то CTS сгенерирует исключение
System.NullReferenceException.