Реализацией интерфейса в Delphi всегда выступает класс. Для этого в объявлении класса необходимо указать, какие интерфейсы он реализует.
type TMyClass = class(TComponent, IMyInterface, IDropTarget) // Реализация методов end;Класс TMyClass реализует интерфейсы IMyInterface и IDropTarget. Необходимо понимать, что реализация классом нескольких интерфейсов не означает множественного наследования и вообще наследования класса от интерфейса. Указание интерфейсов в описании класса означает только то, что в данном классе реализованы все эти интерфейсы.
Класс должен иметь методы, точно соответствующие по именам и спискам параметров всем методам всех объявленных в его заголовке интерфейсов.
Рассмотрим более подробный пример.
type ITest = interface ['{61F26D40-5CE9-11D4-84DD-F1B8E3A70313}'] procedure Beep; end; TTest = class(TInterfacedObject, ITest) procedure Beep; destructor Destroy; override; end; …Здесь класс TTest реализует интерфейс ITest. Рассмотрим использование интерфейса из программы.
procedure TForm1.Button1Click(Sender: TObject); var Test: ITest; begin Test := TTest.Create; Test.Beep; end;Поскольку данный код выглядит довольно странно, остановимся на нем подробнее.
Во-первых, оператор присваивания при приведении типа данных к интерфейсу неявно вызывает метод _AddRef. При этом количество ссылок на интерфейс увеличивается на единицу.
Во-вторых, код не содержит никаких попыток освободить память, выделенную под объект TTest. Тем не менее, если выполнить эту программу, на экран будет выведено сообщение о том, что деструктор был вызван. Это происходит потому, что при выходе переменной, ссылающейся на интерфейс, за область видимости (либо при присвоении ей другого значения) компилятор Delphi генерирует код для вызова метода _Release, информируя реализацию о том, что ссылка на нее больше не нужна.
Внимание! Если у класса запрошен хотя бы один интерфейс — не вызывайте его метод Free (или Destroy). Класс будет освобожден тогда, когда отпадет необходимость в последней ссылке на его интерфейсы. Если вы к этому моменту уничтожили экземпляр класса вручную — произойдет ошибка доступа к памяти. |
Так, следующий код приведет к ошибке в момент выхода из функции:
var Test: ITest; T: TTest; begin T := TTest.Create; Test := T; Test.Beep; T.Free; end; // в этот момент произойдет ошибкаЕсли вы хотите уничтожить реализацию интерфейса немедленно, не дожидаясь выхода переменной за область видимости, – просто присвойте ей значение NIL:
var Test: ITest; T: TTest; begin T := TTest.Create; Test := T; Test.Beep; Test := NIL; // Неявно вызывается IUnknown._Release; end;Обратите особое внимание, что вызовы методов интерфейса IUnknown осуществляются Delphi неявно и автоматически. Поэтому не вызывайте методы интерфейса IUnknown самостоятельно. Это может нарушить нормальную работу автоматического подсчета ссылок и привести к неосвобождению памяти либо к нарушениям защиты памяти при работе с интерфейсами. Во избежание этого необходимо просто помнить следующее.
В рассмотренных примерах код для получения интерфейса у класса генерировался (с проверкой типов) на этапе компиляции. Если класс не реализует требуемого интерфейса, то программа не откомпилируется. Однако существует возможность запросить интерфейс и во время выполнения программы. Для этого служит оператор as, который вызывает QueryInterface и, в случае успеха, возвращает ссылку на полученный интерфейс. В противном случае генерируется исключение.
Например, следующий код будет успешно откомпилирован, но при выполнении вызовет ошибку «Interface not supported»:
var Test: ITest; begin Test := TInterfacedObject.Create as ITest; Test.Beep; end;В то же время код
var Test: ITest; begin Test := TTest.Create as ITest; Test.Beep; end;будет успешно компилироваться и выполняться.
![]() |
![]() |