Вход


Главная страница >> Учебный процесс >> Конспекты >> Delphi. Поддержка технологий COM >> Object Pascal и COM >> Использование интерфейсов

[Назад]    [Содержание ]    [Вперед]

  


Использование интерфейсов


В документации Delphi описывается семантика использования интерфейсов, поэтому iприводить ее здесь не имеет смысла. Вместо этого рассмотрим, как интерфейс IUnknown ?бесшовно? интегрируется в Object Pascal.

Когда переменной интерфейса присваивается значение, компилятор автоматически генерирует вызов метода _AddRef(), и таким образом счетчик ссылок увеличивается. Когда переменная интерфейса выходит за пределы видимости или принимает значение nil компилятор автоматически генерирует вызов метода _Release(). Рассмотрим следующий фрагмент кода:

var
═════I:SomeInteface;
begin
═════I := FunctionThatReturnsAnInterface;
═════I.SomeMethod;
end;

А сейчас обратите внимание на следующий код, который вводит разработчик (выделен полужирным шрифтом), и код, который неявно генерируется компилятором (обычный шрифт) при реализации приведенного ранее фрагмента:

var
═════I: ISomelnterface;
begin
═════// Интерфейс автоматически инициализируется в nil
═════I := nil
═════try
══════════// Ваш код должен быть здесь
══════════I := FunctionThatReturnsAnInterface;
══════════// Метод _AddRef() вызывается неявно, если I определено
══════════I._AddRef;
══════════I.SomeMethod;
═════finnaly
══════════// Неявный финальный блок обеспечивает, что ссылки
══════════// на интерфейс будут удалены
══════════if I <> nil I._Release;
═════end;
end;

Компилятор Delphi также достаточно сообразителен и знает, когда вызванные методы _AddRef() и _Release() переназначаются экземпляру другого интерфейса или принимают значение nil. Рассмотрим такой код:

var
═════I: ISomeInteface;
begin
═════// Назначение I
═════I := FunctionThatReturnsAnInterface;
═════I.SomeMethod;
═════// Переназначение I
═════I := OtherFunctionThatReturnsAnInterface;
═════I.OtherMethod;
═════// Установка I в nil
═════I := nil;
end;

Вновь совместим написанную нами часть кода (полужирный шрифт) с неявно сгенерированным компилятором (обычный). В результате получится следующее:

var
═════I: ISomelnterface;
begin
═════// Интерфейс автоматически инициализируется в nil
═════I := nil
═════try
══════════// Ваш код должен быть здесь
══════════// Назначение I
══════════I := FunctionThatReturnsAnInterface
══════════// Метод _AddRef() вызывается косвенно, если I определено
══════════I._AddRef
══════════I.SomeMethod;
══════════// Переназначение I
══════════I._Release;
══════════I := OtherFunctionThatReturnsAnInterface;
══════════I.OtherMethod;
══════════// Установка I в nil
══════════I._Release;
══════════I := nil;
═════finnaly
══════════// Неявный финальный блок обеспечивает, что ссылки
══════════// на интерфейс будут удалены
══════════if I <> nil I._Release;
═════end;
end;

Приведенный пример помогает понять, почему Delphi использует символ подчеркивания в методах _AddRef() и _Release(). Об увеличении или уменьшении числа ссылок интерфейса часто забывали, и это было классической ошибкой при программировании СОМ в те времена, когда не было ключевого слова interface. Интерфейсы Delphi помогают разработчику, устраняя эти проблемы с помощью неявного вызова этих методов.

Поскольку компилятор знает, как генерировать вызовы _AddRef() и _Release(), может, ему присуще и свойство компилировать третий метод, Querylnterface(), интерфейса IUnknown? Да, он умеет делать и это. Взяв указатель интерфейса для объекта, можно использовать оператор as для подбора интерфейса, поддерживаемого СОМ-объектом. Понятие "подбор" наилучшим образом подходит для описания этого процесса, поскольку такое применение оператора as является нереальным подбором в точном смысле, но внутри него вызывается метод Querylnterface(). Вот демонстрация сказанного выше:

var
═════I1: ISomeInterface;
═════I2: ISomeOtherInterface;
begin
═════// Назначение I1
═════I1 :
= FunctionThatReturnsAnInterface;
═════// Метод QueryInterface I1 для интерфейса I2
═════I2 := I1 as ISomeOtherInterface;
end;

В этом примере исключительная ситуация порождается оператором as, если объект ссылается на I1, который не поддерживается интерфейсом ISomeOtherInterface.

Одно дополнительное правило языка относительно интерфейсов заключается в следующем: переменные интерфейса совместимы по присвоению с классом Object Pascal, который реализует этот интерфейс. Например, рассмотрим следующее объявление интерфейса и класса:;

type
IFoo = interface;
═════//
Определение IFoo
end;
IBar = interface(IFoo)
═════//
Определение Ibar
end;
TBarClass = class(TObject, IBar)
═════//
Определение TbarClass
end;

Используя предыдущие объявления, скорректируем код следующим образом:

var
═════IB: IBar;
═════ТВ: TBarClass;
begin
═════ТВ := TBarClass.Create;
═════try
══════════// Получение ТВ-указателя интерфейса IBar:
══════════IB := ТВ;
══════════// Использование ТВ и IB
═════finally
══════════IB := nil; // Явная выгрузка IB , ТВ.Free;
═════end;
end;

Несмотря на то, что эта особенность, как кажется, нарушает традиционные правила Pascal о совместимости по присвоению, это делает интерфейсы более естественными и простыми в работе. Существует важный, но не очевидный вывод из этих правил ? интерфейсы совместимы по присвоению только с классами, которые явно поддерживают этот интерфейс. Например, класс TBarClass, определенный выше, объявляет явную поддержку интерфейса IBar. Поскольку интерфейс IBar наследуется от интерфейса IFoo, резонно предположить, что интерфейс TBarClass также поддерживает IFoo. Однако это вовсе не так, как иллюстрируется в приведенном ниже фрагменте:

var
═════IF: IFoo;
═════ТВ: TBarClass;
begin
═════ТВ := TBarClass.Create;
═════try
══════════// Ошибки компиляции в следующей строке,
══════════// поскольку TBarClass не поддерживает IFoo явно
══════════IF := ТВ;
══════════// Использование ТВ и IF
═════finally
══════════IF := nil; // Явная выгрузка IF
══════════ТВ.Free;
═════end;
end;



[Назад]    [Содержание ]    [Вперед]

  


  
За содержание страницы отвечает Гончарова М.Н.
©
Кафедра СПиКБ, 2002-2017