В предыдущей главе мы говорили о преобразованиях объектов простых типов. Сейчас затронем тему преобразования объектов классов. Допустим, у нас есть следующая иерархия классов:
abstract class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string lName, string fName)
{
FirstName = fName;
LastName = lName;
}
public abstract void Display();
}
class Employee: Person
{
public string Company { get; set; }
public Employee(string lName, string fName, string comp)
: base(fName, lName)
{
Company = comp;
}
public override void Display()
{
Console.WriteLine(FirstName + " " + LastName + " работает в компании " + Company);
}
}
class Client: Person
{
int _sum; // Переменная для хранения суммы
public string Bank { get; set; }
public Client(string lName, string fName, string comp, int sum)
: base(fName, lName)
{
Bank = comp;
_sum = sum;
}
public override void Display()
{
Console.WriteLine(FirstName + " " + LastName + " имеет счет в банке " +
Bank + " на сумму " + _sum.ToString());
}
}
В этой иерархии классов мы можем проследить следующую цепь наследования: Object (все классы неявно наследуются от типа Object) -> Person -> Employee|Client.
Рассмотрим возможные преобразования типов:
|
|
// Объект Employee также представляет тип object
object emp = new Employee("Bill", "Gates", "Microsoft");
// объект Client также представляет тип Person
Person cl = new Client("Tom", "Johnes", "SberBank", 200);
//у класса Object нет метода Display, поэтому приводим к классу Employee
((Employee)emp).Display();
// у класса Person есть метод Display
cl.Display();
// у класса Person нет свойства Bank, поэтому приводим к классу Client
string bank = ((Client)cl).Bank;
Здесь вначале создаем две переменные типов object и Person, которые хранят ссылки на объекты Employee и Client соответственно. В данном случае работает неявное преобразование, так как наши переменные представляют классы Object и Person, поэтому допустимо неявное восходящее преобразование - преобразование к типам, которые находятся вверху иерархии классов:
Object
|
Person--Client
|
Employee
Однако при применении этих переменных нам придется использовать явное преобразование. Поскольку переменная emp хранит ссылку на объект типа Employee, то мы можем преобразовать к этому типу: ((Employee)emp).Display(). То же самое и с переменной cl.
В то же время мы не можем привести переменную emp к объекту Client, например, так: ((Client)emp).Display();, так как класс Client не находится в иерархии классов между Employee и Object.
Добавим еще класс Manager, который будет производным от Employee и поэтому будет находиться в самом низу иерархии классов:
class Manager: Employee
{
public Manager(string lName, string fName, string comp): base(lName,fName,comp)
{ }
public override void Display()
{
Console.WriteLine(FirstName + " " + LastName + " менеджер компании " + Company);
}
}
Теперь мы можем написать следующее, так как объект класса Manager в то же время является и сотрудником компании (то есть объектом Employee):
|
|
Employee man = new Manager("Джон", "Томпсон", "Google");
man.Display(); // приведение не нужно, так как в классе Employee есть метод Display
Опять же у нас восходящее преобразование Manager к Employee. И так как метод Display есть у класса Employee, нам не надо выполнять преобразование переменной к типу Manager.
Теперь посмотрим на противоположную ситуацию: если мы применим нисходящие преобразования неявно, то мы получим ошибку:
// объект Employee может не представлять объект Manager
Manager man = new Employee("Джон", "Томпсон", "Google");
man.Display();
В данном случае мы пытаемся неявно преобразовать объект Emloyee к типу Manager. Но если Manager является объектом типа Emloyee, то объект Emloyee не является объектом типа Manager. Есть несколько способов избежать ошибки при выполнении программы. Во-первых, можно использовать ключевое слово as. С помощью него программа пытается преобразовать выражение к определенному типу, при этом не выбрасывает исключение. В случае неудачного преобразования выражение будет содержать значение null:
Employee emp = new Employee("Джон", "Томпсон", "Google");
Manager man = emp as Manager;
if (man == null)
{
Console.WriteLine("Преобразование прошло неудачно");
}
else
{
man.Display();
}
Второй способ заключается в отлавливании исключения InvalidCastException, которое возникнет в результате преобразования:
Employee emp = new Employee("Джон", "Томпсон", "Google");
try
{
Manager man = (Manager)emp;
man.Display();
}
catch (InvalidCastException ex)
{
Console.WriteLine(ex.Message);
}
Третий способ заключается в проверке допустимости преобразования с помощью ключевого слова is:
Employee emp = new Employee("Джон", "Томпсон", "Google");
if(emp is Manager)
{
((Manager)emp).Display();
}
else
{
Console.WriteLine("Преобразование не допустимо");
}
Выражение emp is Manager проверяет, является ли переменная emp объектом типа Manager. Но так как в данном случае явно не является, то такая проверка вернет значение false, и преобразование не сработает.