Абстрактные методы
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 вместо полиморфизма является плохим стилем программирования, т.к. приводит к снижению производительности программы.
Проверка корректности преобразования типов требует прохождения по всей иерархии классов во время выполнения программы, а для обращения к виртуальному методу требуется только прочитать адрес этого метода из памяти.