Преобразование типов

Последнее обновление: 05.08.2021

Довольно частой задачей при работе с классами является преобразование типов. Допустим, у нас есть следующая иерархия классов:

type Person(name) =  
    member this.Name = name

type Employee(name, company) = 
    inherit Person(name)
    member this.Company = company

В этой иерархии классов мы можем проследить следующую цепь наследования: Object (все классы неявно наследуются от типа Object) -> Person -> Employee|Client.

Иерархия классов в языке программирования F#

Причем в этой иерархии классов базовые типы находятся вверху, а производные типы - внизу.

Восходящие преобразования. Upcasting

Объекты производного типа (который находится внизу иерархии) в то же время представляют и базовый тип. Например, объект 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

Кроме восходящих преобразований от производного к базовому типу есть нисходящие преобразования или 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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850