Страница 1 из 1
Приведение объекта к интерфейсу
Добавлено: 12.05.2010 10:57:54
Climber
Кажется, похожее кто-то уже спрашивал, но кто и где, не помню.
У меня есть интерфейс и объект, который его реализует:
Код: Выделить всё
type
ISubscriber = interface
procedure Notify(AEvent: string);
end;
{ TSubscriber }
TSubscriber = class (TSomeClass, ISubscriber)
public
procedure Notify(AEvent: string); virtual;
end;
А еще у меня есть список TStringList, в который добавляются ссылки на эти объекты (причем объекты могут быть разными и не иметь общего предка, но они все наследники от TObject и теоретически реализуют интерфейс ISubscriber. Я даже морально готов получать AV, если интерфейс не реализован в объекте в списке). И я хочу вызывать этот интерфейс из списка:
Код: Выделить всё
TSubscriber (FList.Objects[i]).Notify(AEvent); // 1. это работает
ISubscriber(FList.Objects[i]).Notify(AEvent); // 2. это не работает - "Class or Object types "TObject" and "ISubscriber" are not related"
Способ 2 не компилируется.
В
этом топике написано, что директива {$OBJECTCHECKS OFF} должна решать проблему, но у меня не решает.
Можно эту проблему решить или придется делать всем объектам общего предка, реализующего интерфейс, и приводить к нему?
Re: Приведение объекта к интерфейсу
Добавлено: 12.05.2010 11:38:11
Sergei I. Gorelkin
Либо через общего предка, либо с помощью (FList.Objects[i] as ISubscriber).Notify(AEvent);
В первом случае тебе и интерфейс не нужен. Во втором случае (с COM-интерфейсами) объект может быть уничтожен, если на него нет других ссылок.
Re: Приведение объекта к интерфейсу
Добавлено: 12.05.2010 11:57:14
Climber
Sergei I. Gorelkin писал(а): либо с помощью (FList.Objects[i] as ISubscriber).Notify(AEvent);
Хм... Добавил GUID и скомпилировалось...
Я всегда думал, что
ISubscriber(FList.Objects[i]) и
(FList.Objects[i] as ISubscriber) - это эквивалентные конструкции
Поэтому даже и попробовать не догадался...
Добавлено спустя 5 минут 56 секунд:Извиняюсь, поспешил...
Компилируется, но при выполнении возникает ошибка "Invalid Type Cast"...
Re: Приведение объекта к интерфейсу
Добавлено: 12.05.2010 12:55:46
Sergei I. Gorelkin
Invalid type cast означает, что таки не удается получить нужный интерфейс из имеющегося объекта.
Боюсь, без полного примера ничего более конкретного сказать не смогу...
Re: Приведение объекта к интерфейсу
Добавлено: 12.05.2010 13:50:52
Climber
Полный пример - запросто.
Я пытаюсь сделать реализацию шаблона Publisher - Subscriber. Готовые реализации в количестве 1 штуки для Delphi видел краем глаза, и она мне не понравилась.
Подписчик:
Код: Выделить всё
{$OBJECTCHECKS ON}
{$INTERFACES CORBA}
interface
uses Classes, SysUtils, Forms;
type
{ ISubscriber }
ISubscriber = interface
['{7B3C558B-00BE-424D-91AC-5F9D894CC51B}']
procedure Notify(AEvent: string);
end;
{ TSubscriber }
TSubscriber = class (TForm, ISubscriber) // TForm взят с потолка, можно подставить любой другой класс по вкусу...
public
procedure Notify(AEvent: string); virtual;
end;
implementation
{ TSubscriber }
procedure TSubscriber.Notify(AEvent: string);
begin
end;
Publisher:
Код: Выделить всё
uses
Classes, SysUtils, Subscriber;
type
{ TPublisher }
TPublisher = class
private
FList: TStringList;
public
constructor Create;
destructor Destroy; override;
function SubscriptionIndex(ASubscriber: TSubscriber; AEvent: string): longint;
procedure Subscribe(ASubscriber: TSubscriber; AEvent: string); virtual;
procedure Unsubscribe(ASubscriber: TSubscriber; AEvent: string); virtual;
procedure Notify(AEvent: string); virtual;
end;
implementation
procedure TPublisher.Notify(AEvent: string);
{ Уведомляет всех подписчиков на событие AEvent о том, что оно случилось. }
var i: longint;
begin
for i:=0 to FList.Count-1 do
if AEvent=FList.Strings[i] then
(FList.Objects[i] as ISubscriber).Notify(AEvent); // Ошибка тут
end;
Общий принцип работы
Publisher'a: подписчики подписываются на определенные события (название события хранятся как строки). В Publisher'e есть список TStringList, который хранит набор строк и связанных с ними объектов. Таким образом каждый объект может подписаться на несколько разных событий и получить информацию о том, какое именно случилось. Остальные методы не привожу, чтобы не загромождать текст.
Вот код тестирующей процедуры:
Код: Выделить всё
procedure TPublisherTestCase.NotifyTest;
var event: string;
s1, s2, s3: TTestSubscriber;
begin
s1:=TTestSubscriber.Create(nil);
s2:=TTestSubscriber.Create(nil);
s3:=TTestSubscriber.Create(nil);
p.Subscribe(s1, 'event1');
p.Subscribe(s2, 'event2');
p.Subscribe(s3, 'event3');
p.Subscribe(s2, 'event4');
p.Notify('event2');
AssertEquals('Subscriber was not notified', 'event2', s2.NotifyRecieved);
FreeAndNil(s1);
FreeAndNil(s2);
FreeAndNil(s3);
end;
Во вложении - проект для fpcunit, там полные тексты обоих модулей и тесткейс для тестирования класса.
Добавлено спустя 5 минут 29 секунд:P. S. TTestSubscriber - потомок TSubscriber, но сути проблемы это не меняет.
Re: Приведение объекта к интерфейсу
Добавлено: 12.05.2010 15:17:26
Sergei I. Gorelkin
Проверил. С транковой версией FPC от 13 марта сего года работает.
Видимо, будет работать и для более старых версий, если не использовать corba-интерфейсы.
Оператор 'as' для corba долгое время не работал, его пофиксили сравнительно недавно.
Re: Приведение объекта к интерфейсу
Добавлено: 13.05.2010 11:12:13
Climber
О, кажется, заработало!
Тогда самый последний вопрос (для общего образования).
Сначала я сделал интерфейс и реализовал его в объекте.
Компилятор потребовал дописать реализации методов
_AddRef и
_DelRef (там еще третий был, не помню). Я полез разбираться и нашел, что это лечится директивой
{$INTERFACES CORBA}. Вставил ее, компилятор перестал требовать эти методы.
Потом я попытался применить конструкцию
FList.Objects[i] as ISubscriber и узнал от компилятора, что мне нужен GUID. Сгенерировал его (заодно узнал, что это такое

), потом убрал директиву
{$INTERFACES CORBA}. Компилятор не стал требовать методы
_AddRef и
_DelRef. Самое интересное, что теперь я убираю GUID, а
_AddRef и
_DelRef все равно не нужны.
Что там на самом деле происходило?
И еще:
{$OBJECTCHECKS ON} что дает? Видел в примерах, но пока не понял сути явления...