Принципы объектно-ориентированного программирования

         

Приведение типов



Приведение типов

Сейчас мы рассмотрим один из самых важных аспектов типов — приведение. Допустим, существует базовый класс под названием 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.

 

Содержание раздела