Безопасное преобразование типов

Абстрактные методы

Abstract, нужны для полиморфизма.

Type Tanimal=class

……………

Function Voice:string;virtual;abstract;

Private

Kind:string;

End;

TDog = class(Tanimal);

Function Voice:string;override;

Function Eat:string; virtual;

…..

End;

TCat = class(TAnimal)

……….

Function Voice:string; override;

Function Eat:string; virtual;

….

End;

Каждый наследник Tanimal переопределяет виртуальный абстрактный метод Voice и добавляет новый метод Eat.

LbVoice.Caption:=myAnimal.Voice;

LbEatCaption:=myAnimal.Eat;// error!

Для решения данный проблемы переменная myAnimal содержит либо TCat либо TDog, соответ.треб.вызвать их метод Eat. Можно воспользоваться механизмом определения типа на этапе выполнения RTTI RunTime Type Information и с его помощью привести объект типа Tanimal к типу TDog или TCat, в зависимости от того, чем он сейчас является на самом деле.

Но добавление абстрактного метода в суперкласс является типичным решением такой проблемы.

Класс TDog имеет метод Eat, остутствующий в классе TAnimal. Если myAnimal ссылается на экземпляр класса TDog, требуется возможность вызвать эту функцию.

1) прямое приведение типов

Если его сделать а переменная вдруг содержит экземплр другого типа, то можно получить ошибку времени выполнения или непредсказуемое изменение содержимого в памяти, т.к. компилятор не может определить является ли тип объекта конкретным и существует ли в действительности вызываемый метод.

В данном случае можно воспользоваться механизмом RTTI, каждый объект знает свой тип а также класс своего родителя. И можно обратиться к этой информации, используя оператор is или метод InheritsFrom класса TObject.

Парметрами операции is является объект и тип класса, а результат bool.

///

If myAnimal is TDog then…

////

Результат истина если переменная myAnimal ссылается в данный момент на экземпляр класса TDog или его потомка!!!

После того как мы убедились как переменная myAnimal совместима с типом TDog можно выполнить прямое приведение типов

If myAnimal is TDog then

Begin mydog:=TDog(myAnimal);

LbEat.Caption:=myDog.Eat; //LbEat:=TDog(myAnimal).Eat

Два действия (is и прямое приведение типов) можно объединить на одну операцию as, которая делает то же самое

Mydog:=myAnimal as TDog;

LbEat.Caption:=myDog.Eat;

LbEat.Caption:=(myAnimal as TDog).Eat; //осущесвтялет проверку типа и потом выполняет преобразование типа, но если не тот экземпляр то все плохо

……

If myAnimal is TDog then

(myAnimal as TDog).Eat; // так очень длинно

Вывод: сначало проверим, а потом вручную прямое приведение типов!

Разница между традиционным преобразованием типов и использованием оператора as заключается в том, что последний вариант вызовет исключающую ситуацию, если тип объекта не совпадает с типом класса, к которому его пытаются привезти. Чтобы избежать этой ситации лучше всего использовать операцию is а потом прямое приведение типов(чтобы не выполнять is 2 раза).

Обе операции is и as часто применяются в Дельфи, когда возникает необходимость в написании кода универсального для нескольких компонентов одного типа или даже разных типов, когда компонент передается как параметр методу, обрабатывающему событие.

Пример

Procedure Tform1.Btn1Click(Sendet:Tobject);

Begin

If sender is //button///TEdit//// then…..

End;

Это стандартный прием в Дельфи, некая конструкция программирование, но несмотря на этой следует ограничить применение этих операций лишь несколькими специальными случаями.

В случае, когда есть выбор между полиморфизмом и RTTI предпочтение следует отдавать полиморфизму.

Использование механизма RTTI вместо полиморфизма является плохим стилем программирования, т.к. приводит к снижению производительности программы.

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


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: