У меня есть объект, который делегирует реализацию особенно сложного интерфейса дочернему объекту. Этот точно , я думаю, что это работа TAggregatedObject
. Объект « child » содержит слабую ссылку на свой контроллер « », и все запросы QueryInterface
передаются обратно родителю.
всегда один и тот же объект.
Итак, мой родительский (т.е. «Контроллер» ) объект объявляет, что он реализует интерфейс IStream
:
type
TRobot = class(TInterfacedObject, IStream)
private
function GetStream: IStream;
public
property Stream: IStream read GetStrem implements IStream;
end;
Примечание: Это гипотетический пример. я выбрал слово
робот
потому что это звучит сложно, и и слово длиной всего 5 букв - это короткая. я также выбралIStream
, потому что это коротко. я собирался использоватьIPersistFile
илиIPersistFileInit
, но они длиннее и делают Пример кода сложнее реального. В другом слова: это гипотетический пример.
Теперь у меня есть мой дочерний объект, который будет реализовывать IStream
:
type
TRobotStream = class(TAggregatedObject, IStream)
public
...
end;
Все, что осталось, и именно здесь начинается моя проблема: создание RobotStream
когда запрашивается:
function TRobot.GetStream: IStream;
begin
Result := TRobotStream.Create(Self) as IStream;
end;
Этот код не компилируется с ошибкой Оператор не применим к этому типу операнда.
.
Это потому, что delphi пытается выполнить как IStream
для объекта, который не реализует IUnknown
:
TAggregatedObject = class
...
{ IUnknown }
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
...
Методы IUnknown могут быть там, но объект не объявляет , что он поддерживает IUnknown
. Без интерфейса IUnknown
Delphi не может вызвать QueryInterface
для выполнения приведения.
Поэтому я изменяю свой класс TRobotStream
, чтобы объявить, что он реализует отсутствующий интерфейс (что он делает; он наследует его от своего предка):
type
TRobotStream = class(TAggregatedObject, IUnknown, IStream)
...
И теперь он компилируется, но вылетает во время выполнения на строка:
Result := TRobotStream.Create(Self) as IStream;
Теперь я могу видеть , что происходит , но я не могу объяснить , почему . Delphi вызывает IntfClear
для моего родительского объекта Robot
на выходе из конструктора дочернего объекта.
Я не знаю, как правильно предотвратить это. я мог бы попытаться вызвать актерский состав:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
и надеяться, что он сохранит ссылку. Оказывается, он сохраняет ссылку - без сбоев на выходе из конструктора.
Примечание: Меня это смущает. Поскольку я прохожу объект где он наследует его от своего предка):
type TRobotStream = class(TAggregatedObject, IUnknown, IStream) ...
И теперь он компилируется, но вылетает во время выполнения в строке:
Result := TRobotStream.Create(Self) as IStream;
Теперь я могу видеть , что происходит , но я не могу объяснить , почему . Delphi вызывает
IntfClear
для моего родительского объектаRobot
на выходе из конструктора дочернего объекта.Я не знаю, как правильно предотвратить это. я мог бы попытаться вызвать актерский состав:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
и надеяться, что он сохранит ссылку. Оказывается, он сохраняет ссылку - без сбоев на выходе из конструктора.
Примечание: Меня это смущает. Поскольку я прохожу объект где он наследует его от своего предка):
type TRobotStream = class(TAggregatedObject, IUnknown, IStream) ...
И теперь он компилируется, но вылетает во время выполнения в строке:
Result := TRobotStream.Create(Self) as IStream;
Теперь я могу видеть , что происходит , но я не могу объяснить , почему . Delphi вызывает
IntfClear
для моего родительского объектаRobot
на выходе из конструктора дочернего объекта.Я не знаю, как правильно предотвратить это. я мог бы попытаться вызвать актерский состав:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
и надеяться, что он сохранит ссылку. Оказывается, он сохраняет ссылку - без сбоев на выходе из конструктора.
Примечание: Меня это смущает. Поскольку я прохожу объект где объяснить , почему . Delphi вызывает
IntfClear
для моего родительского объектаRobot
на выходе из конструктора дочернего объекта.Я не знаю, как правильно предотвратить это. я мог бы попытаться вызвать актерский состав:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
и надеяться, что он сохранит ссылку. Оказывается, он сохраняет ссылку - без сбоев на выходе из конструктора.
Примечание: Меня это смущает. Поскольку я прохожу объект где объяснить , почему . Delphi вызывает
IntfClear
для моего родительского объектаRobot
на выходе из конструктора дочернего объекта.Я не знаю, как правильно предотвратить это. я мог бы попытаться вызвать актерский состав:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
и надеяться, что он сохранит ссылку. Оказывается, он сохраняет ссылку - без сбоев на выходе из конструктора.
Примечание: Меня это смущает. Поскольку я прохожу объект где Это меня смущает. Поскольку я прохожу объект где Это меня смущает. Поскольку я прохожу объект где ожидается интерфейс . я мог бы Предположим, что компилятор неявно предварительное формирование типа, например:
Результат: = TRobotStream.Create (Self
как IUnknown);
для удовлетворения вызова. Дело в том, что проверка синтаксиса не пожаловаться позвольте мне предположить, что все было правильно.
Но аварии не закончились. я изменил строку на:
Result := TRobotStream.Create(Self as IUnknown) as IStream;
И код действительно возвращается из конструктора
TRobotStream
без уничтожения моего родительского объекта, но теперь я получаю переполнение стека.Причина в том, что
TAggregatedObject
откладывает всеQueryInterface
(то есть приведение типов) обратно к родительскому объекту. В моем случае я преобразуюTRobotStream
вIStream
.Когда я спрашиваю у
TRobotStream
егоIStream
в конце из:Result := TRobotStream.Create(Self as IUnknown) as IStream;
Он поворачивается и запрашивает у своего контроллера интерфейс
IStream
, который запускает вызов:Result := TRobotStream.Create(Self as IUnknown) as IStream; Result := TRobotStream.Create(Self as IUnknown) as IStream;
, который поворачивается и вызывает:
Result := TRobotStream.Create(Self as IUnknown) as IStream; Result := TRobotStream.Create(Self as IUnknown) as IStream; Result := TRobotStream.Create(Self as IUnknown) as IStream;
Boom! Переполнение стека.
Слепо, я пытаюсь удалить окончательное приведение к
IStream
, пусть Delphi попытается неявно привести объект к интерфейсу (который я только что видел выше, не работает правильно):Result := TRobotStream.Create(Self as IUnknown);
И теперь нет аварийного завершения; что я не очень понимаю это. Я построил объект, объект, который поддерживает несколько интерфейсов. Как теперь Delphi знает, как использовать интерфейс? Выполняет ли он правильный подсчет ссылок? Я видел выше, что это не так. Есть ли небольшая ошибка, ожидающая сбоя для клиента?
Так что у меня осталось четыре возможных способа позвонить по одной линии. Какой из них действителен?
Результат: = TRobotStream.Create (Self);
Результат: = TRobotStream.Create (Self as IUnknown);
Результат: = TRobotStream.Create (Self) как IStream;
Результат: = TRobotStream Создать (Self как IUnknown) как IStream;
Настоящий вопрос
Я обнаружил немало тонких ошибок и трудных для понимания тонкостей компилятора. Это приводит меня к мысли, что я все сделал совершенно неправильно. При необходимости проигнорируйте все, что я сказал, и помогите мне ответить на вопрос:
Как правильно делегировать реализацию интерфейса дочернему объекту?
Может быть, мне следует использовать
TContainedObject
вместоTAggregatedObject
. Возможно, они работают в тандеме, где родитель должен бытьTAggregatedObject
, а дочерний -TContainedObject
. Может быть, это наоборот. Может быть, ни не применимы в этом случае.Примечание: Все в основной части моего поста можно игнорировать. Это было просто и сложные для понимания тонкости компилятора. Это приводит меня к мысли, что я все сделал совершенно неправильно. При необходимости проигнорируйте все, что я сказал, и помогите мне ответить на вопрос:
Как правильно делегировать реализацию интерфейса дочернему объекту?
Может быть, мне следует использовать
TContainedObject
вместоTAggregatedObject
. Возможно, они работают в тандеме, где родитель должен бытьTAggregatedObject
, а дочерний -TContainedObject
. Может быть, это наоборот. Может быть, ни не применимы в этом случае.Примечание: Все в основной части моего поста можно игнорировать. Это было просто и сложные для понимания тонкости компилятора. Это приводит меня к мысли, что я все сделал совершенно неправильно. При необходимости проигнорируйте все, что я сказал, и помогите мне ответить на вопрос:
Как правильно делегировать реализацию интерфейса дочернему объекту?
Может быть, я должен использовать
TContainedObject
вместоTAggregatedObject
. Возможно, они работают в тандеме, где родитель должен бытьTAggregatedObject
, а дочерний -TContainedObject
. Может быть, это наоборот. Может быть, ни не применимы в этом случае.Примечание: Все в основной части моего поста можно игнорировать. Это было просто и помогите мне ответить на вопрос:
Как правильно делегировать реализацию интерфейса дочернему объекту?
Может быть, мне следует использовать
TContainedObject
вместоTAggregatedObject
. Возможно, они работают в тандеме, где родитель должен бытьTAggregatedObject
, а дочерний -TContainedObject
. Может быть, это наоборот. Может быть, ни не применимы в этом случае.Примечание: Все в основной части моего поста можно игнорировать. Это было просто и помогите мне ответить на вопрос:
Как правильно делегировать реализацию интерфейса дочернему объекту?
Может быть, мне следует использовать
TContainedObject
вместоTAggregatedObject
. Возможно, они работают в тандеме, где родитель должен бытьTAggregatedObject
, а дочерний -TContainedObject
. Может быть, это наоборот. Может быть, ни не применимы в этом случае.Примечание: Все в основной части моего поста можно игнорировать. Это было просто где родитель должен быть
TAggregatedObject
, а дочерний -TContainedObject
. Может быть, это наоборот. Может быть, ни не применимы в этом случае.Примечание: Все в основной части моего поста можно игнорировать. Это было просто где родитель должен быть
TAggregatedObject
, а дочерний -TContainedObject
. Может быть, это наоборот. Может быть, ни не применимы в этом случае.Примечание: Все в основной части моего поста можно игнорировать. Это было просто чтобы показать, что я думал об этом. Есть те, кто будет утверждать, что включая то, что я пробовал, у меня есть отравлены возможные ответы; скорее чем отвечать на мой вопрос, люди может сосредоточиться на моем неудачном вопросе.
Настоящая цель - делегировать интерфейс реализация дочернего объекта. Эта вопрос содержит мои подробные попытки при решении проблемы с
TAggregatedObject
. Вы даже не см. два других моих решения. Один из которых страдает от кругового Ссылка имеет значение, и разрывыIНеизвестное
правило эквивалентности.Роб Кеннеди мог бы вспомнить; и спросил мне сделать вопрос, который просит решение проблемы, а не решение проблемы в одном из моих решения.
Редактировать: грамматизировать
Редактировать 2: Нет такой вещи, как контроллер робота. Ну, есть - я все время работал с контроллерами Funuc RJ2. Но не в этом примере!
Редактировать 3 *
TRobotStream = class(TAggregatedObject, IStream) public { IStream } function Seek(dlibMove: Largeint; dwOrigin: Longint; out libNewPosition: Largeint): HResult; stdcall; function SetSize(libNewSize: Largeint): HResult; stdcall; function CopyTo(stm: IStream; cb: Largeint; out cbRead: Largeint; out cbWritten: Largeint): HResult; stdcall; function Commit(grfCommitFlags: Longint): HResult; stdcall; function Revert: HResult; stdcall; function LockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall; function UnlockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall; function Stat(out statstg: TStatStg; grfStatFlag: Longint): HResult; stdcall; function Clone(out stm: IStream): HResult; stdcall; function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall; function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall; end; TRobot = class(TInterfacedObject, IStream) private FStream: TRobotStream; function GetStream: IStream; public destructor Destroy; override; property Stream: IStream read GetStream implements IStream; end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.Button1Click(Sender: TObject); var rs: IStream; begin rs := TRobot.Create; LoadRobotFromDatabase(rs); //dummy method, just to demonstrate we use the stream rs := nil; end; procedure TForm1.LoadRobotFromDatabase(rs: IStream); begin rs.Revert; //dummy method call, just to prove we can call it end; destructor TRobot.Destroy; begin FStream.Free; inherited; end; function TRobot.GetStream: IStream; begin if FStream = nil then FStream := TRobotStream.Create(Self); result := FStream; end;
Проблема здесь в том, что «родительский» объект
TRobot
уничтожается во время вызова:FStream := TRobotStream.Create(Self);
Вы должны добавить экземпляр поля для созданного дочернего объекта:
type
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
property Stream: IStream read GetStream implements IStream;
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
Обновление Как вы уже догадались, TRobotStream должен быть производным от TAggregatedObject. Объявление должно быть таким:
type
TRobotStream = class(TAggregatedObject, IStream)
...
end;
Необязательно упоминать IUnknown.
В TRobot.GetStream строка result: = FStream
выполняет неявный FStream as IStream
, поэтому записывать это тоже не нужно.
FStream должен быть объявлен как TRobotStream, а не как IStream, чтобы его можно было уничтожить при уничтожении экземпляра TRobot. Примечание. TAggregatedObject не имеет подсчета ссылок, поэтому контейнер должен заботиться о своем сроке службы.
Обновление (код Delphi 5):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, activex, comobj;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
procedure LoadRobotFromDatabase(rs: IStream);
public
end;
type
TRobotStream = class(TAggregatedObject, IStream)
public
{ IStream }
function Seek(dlibMove: Largeint; dwOrigin: Longint;
out libNewPosition: Largeint): HResult; stdcall;
function SetSize(libNewSize: Largeint): HResult; stdcall;
function CopyTo(stm: IStream; cb: Largeint; out cbRead: Largeint; out cbWritten: Largeint): HResult; stdcall;
function Commit(grfCommitFlags: Longint): HResult; stdcall;
function Revert: HResult; stdcall;
function LockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function UnlockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function Stat(out statstg: TStatStg; grfStatFlag: Longint): HResult; stdcall;
function Clone(out stm: IStream): HResult; stdcall;
function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall;
function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall;
end;
type
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
destructor Destroy; override;
property Stream: IStream read GetStream implements IStream;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
rs: IStream;
begin
rs := TRobot.Create;
LoadRobotFromDatabase(rs); //dummy method, just to demonstrate we use the stream
rs := nil;
end;
procedure TForm1.LoadRobotFromDatabase(rs: IStream);
begin
rs.Revert; //dummy method call, just to prove we can call it
end;
function TRobotStream.Clone(out stm: IStream): HResult;
begin
end;
function TRobotStream.Commit(grfCommitFlags: Integer): HResult;
begin
end;
function TRobotStream.CopyTo(stm: IStream; cb: Largeint; out cbRead, cbWritten: Largeint): HResult;
begin
end;
function TRobotStream.LockRegion(libOffset, cb: Largeint; dwLockType: Integer): HResult;
begin
end;
function TRobotStream.Read(pv: Pointer; cb: Integer; pcbRead: PLongint): HResult;
begin
end;
function TRobotStream.Revert: HResult;
begin
end;
function TRobotStream.Seek(dlibMove: Largeint; dwOrigin: Integer;
out libNewPosition: Largeint): HResult;
begin
end;
function TRobotStream.SetSize(libNewSize: Largeint): HResult;
begin
end;
function TRobotStream.Stat(out statstg: TStatStg; grfStatFlag: Integer): HResult;
begin
end;
function TRobotStream.UnlockRegion(libOffset, cb: Largeint; dwLockType: Integer): HResult;
begin
end;
function TRobotStream.Write(pv: Pointer; cb: Integer; pcbWritten: PLongint): HResult;
begin
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
end.
Нет необходимости, чтобы ваш класс, выполняющий делегирование, унаследовал от какого-либо конкретного класса. Вы можете унаследовать от TObject, если были реализованы соответствующие методы. Я буду упрощать и проиллюстрирую использование TInterfacedObject, который предоставляет 3 основных метода, которые вы уже определили.
Также не требуется TRobotStream = class (TAggregatedObject, IUnknown, IStream)
. Вместо этого вы можете просто объявить, что IStream наследуется от IUnknown. Кстати, я всегда даю своим интерфейсам GUID (нажмите сочетание Ctrl + Shift + G).
Существует ряд различных подходов и техник, которые можно применить в зависимости от ваших конкретных потребностей.
Простейшее делегирование - по интерфейсу.
TRobotStream = class(TinterfacedObject, IStream)
TRobot = class(TInterfacedObject, IStream)
private
//The delegator delegates the implementations of IStream to the child object.
//Ensure the child object is created at an appropriate time before it is used.
FRobotStream: IStream;
property RobotStream: IStream read FRobotStream implements IStream;
end;
Возможно, есть несколько вещей, на которые следует обратить внимание: