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