Довольно частой задачей при работе с классами является преобразование типов. Допустим, у нас есть следующая иерархия классов:
type Person(name) =
member this.Name = name
type Employee(name, company) =
inherit Person(name)
member this.Company = company
В этой иерархии классов мы можем проследить следующую цепь наследования: Object (все классы неявно наследуются от типа Object) -> Person -> Employee|Client.
Причем в этой иерархии классов базовые типы находятся вверху, а производные типы - внизу.
Объекты производного типа (который находится внизу иерархии) в то же время представляют и базовый тип. Например, объект Employee в то же время является и объектом класса Person. Что в принципе естественно, так как каждый сотрудник (Employee) является человеком (Person). И мы можем написать, например, следующим образом:
type Person(name) =
member this.Name = name
type Employee(name, company) =
inherit Person(name)
member this.Company = company
let printPerson(person: Person) = printfn $"{person.Name}"
let bob = Person("Bob")
let tom = Employee("Tom", "Microsoft")
printPerson(bob) // Bob
printPerson(tom) // Tom
Здесь определена функция printPerson, которая в качестве параметра принимает объект Person. И поскольку объекты Employee также представляют ти Person,
то мы можем и их также передавать в эту функцию.
Кроме восходящих преобразований от производного к базовому типу есть нисходящие преобразования или downcasting - от базового типа к
производному. В предыдущем примере функция printPerson выводила только имя человека. Но что, если мы хотим также выводить и компанию,
если человек является сотрудником. Если мы, к примеру, напишем следующим образом, то мы столкемся с ошибкой:
let printPerson(person: Person) =
printfn $"Name: {person.Name}"
printfn $"Company: {person.Company}" // Ошибка - в классе Person нет свойства Company
Для компилятора параметр представляет тип Person, у которого нет свойства Company. Что делать в этом случае? Нам надо выполнить преобразование типов. Для этого применяется оператор :?>:
объект :?> тип
Слева от оператора идет объект, который надо преобразовать, а справа тип, к которому надо преобразовать. Оператор возвращает преобразованный к указанному типу объект. Применим данный оператор:
type Person(name) =
member this.Name = name
type Employee(name, company) =
inherit Person(name)
member this.Company = company
let printPerson(person: Person) =
printfn $"Name: {person.Name}"
// преобразуем объект person к типу Employee
let employee = person :?> Employee
printfn $"Company: {employee.Company}"
let tom = Employee("Tom", "Microsoft")
printPerson(tom) // Name: Tom
// Company: Microsoft
В функции printPerson преобразуем объект person к типу Employee и получаем результат преобразования в значение employee. Далее мы сможем работать с
этим значением как значением типа Employee, в том числе получить его свойство Company:
let employee = person :?> Employee
printfn $"Company: {employee.Company}"
Однако что, если мы передадим в эту функцию объект типа Person, у которого нет свойства Company:
let bob = Person("Bob")
printPerson(bob) // Ошибка !
В этом случае мы столкнемся с ошибкой, так как среда не сможет преобразовать объект person к типу Employee. Преобразование к типу можно выполнить если только объект представляет
этот тип. Но объект bob НЕ представляет объект Employee. Что делать в этой ситуации? Здесь мы предварительно можем проверить, что объект представляет данный тип.
Для проверки типа объекта применяется оператор :?:
объект :? тип
Слева от оператора идет объект, который надо проверить, а справа - тип данных. Если объект представляет этот тип, то оператор возвращает true,
если нет, то возвращается false.
Исправим предыдущий пример с учетом данного оператора:
type Person(name) =
member this.Name = name
type Employee(name, company) =
inherit Person(name)
member this.Company = company
let printPerson(person: Person) =
printfn $"Name: {person.Name}"
// если person представляет тип Employee
if person :? Employee then
// преобразуем объект person к типу Employee
let employee = person :?> Employee
printfn $"Company: {employee.Company}"
let tom = Employee("Tom", "Microsoft")
printPerson(tom)
let bob = Person("Bob")
printPerson(bob)
Консольный вывод программы:
Name: Tom Company: Microsoft Name: Bob