Предыдущая серия была тут - http://programmingmindstream.blogspot.ru/2014/04/supports.html
Поступил тут вопрос:
http://programmingmindstream.blogspot.ru/2014/04/supports.html?showComment=1396620657041#c4522417805967527391
"Извините но я не понимаю как мы ожидаем С. Если это метод класса TC. Мы же даже объект не создаем ?"
Разовью тему.
Итак был пример:
Теперь напишем ТАК:
Мысль понятна?
Изменилась ОДНА строчка. А КАКОВА РАЗНИЦА!
Продолжим.
Напишем теперь так:
УДИВИТЕЛЬНО!
Не правда ли?
НЕСИММЕТРИЯ метода Supports - ОЧЕВИДНА.
По-моему...
"Сухой остаток":
overload - "вреден", "вообще" и в ДАННОМ случае "в частности".
Особенно вреден overload с "ковариантными" типами.
Да и вообще говоря, можно было бы обойтись (на месте Borland'а) ТОЛЬКО методом Supports(IUnknown), а не "ГОРОДИТЬ" ещё один КРАЙНЕ НЕОЧЕВИДНЫЙ метод Supports(TObject).
Мысль понятна?
Сразу оговорюсь - "тема далеко не для всех".
Посему не бросайтесь комментировать, а сначала - "вкурите" проблему.
Надеюсь, что помог кому-то.
P.S. Если подобные "экзерсисы" с объектами vs. интерфейсы - ИНТЕРЕСНЫ - пишите. У меня "в кармане" ещё есть "запасец".
P.P.S. Ну и я надеюсь понятно, что GUID'ы в описании интерфейсов - ОПУЩЕНЫ. Их можно вставить по Crtl-Shift-G.
P.P.P.S. Ну и "в тему":
http://18delphi.blogspot.ru/2013/11/queryinterface.html
http://18delphi.blogspot.ru/2013/11/supports.html
http://18delphi.blogspot.ru/2013/10/supports.html
P.P.P.P.S Один из читателей любезно предоставил компилируемый пример - https://bitbucket.org/ingword/temp/src
Поступил тут вопрос:
http://programmingmindstream.blogspot.ru/2014/04/supports.html?showComment=1396620657041#c4522417805967527391
"Извините но я не понимаю как мы ожидаем С. Если это метод класса TC. Мы же даже объект не создаем ?"
Разовью тему.
Итак был пример:
type
 ISomeInterface = interface
  procedure SomeMethod;
 end;//ISomeInterface
 TA = class(TObject, ISomeInterface {IUnknown тут СПЕЦИАЛЬНО опущен})
  function _AddRef: Integer;
  function _Release: Integer;
  function QueryInterface(const anID: TGUID; out anObj): hResult; virtual; 
  procedure SomeMethod;
 end;//TA
 TB = class(TA)
  function QueryInterface(const anID: TGUID; out anObj): hResult; override;
 end;//TB
 TC = class(TIntefacedObject, ISomeInterface)
  procedure SomeMethod;
 end;//TC
...
function TA._AddRef: Integer;
begin
 Result := -1;
end;
function TA._Release: Integer;
begin
 Result := -1;
end;
function TA.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if Self.GetInterface(anID, anObj) then
  Result := S_Ok
 else
  Result := E_NoInterface;
end;
procedure TA.SomeMethod;
begin
 Write('A');
end;
function TB.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if IsEqualGUID(anID, ISomeInterface) then
 begin
  Result := S_Ok;
  ISomeInterface(Obj) := TC.Create;
 end//IsEqualGUID(anID, ISomeInterface)
 else
  Result := inherited QueryInterface(anID, Obj);  
end;
procedure TC.SomeMethod;
begin
 Write('C');
end;
...
var
 l_A : ISomeInterface;
 l_B : ISomeInterface;
 A : TA;
 B : TB;
begin
 A := TA.Create;
 B := TB.Create;
 if not Supports(A, ISomeInterface, l_A) then
  Assert(false);
 l_A.SomeMethod; // - в консоли видим A
 if not Supports(B, ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - в консоли видим A, а "хотелось бы" - C
end;
Теперь напишем ТАК:
type
 ISomeInterface = interface
  procedure SomeMethod;
 end;//ISomeInterface
 TA = class(TObject, IUnknown {тут ПОЯВИЛСЯ IUnknown}, ISomeInterface)
  function _AddRef: Integer;
  function _Release: Integer;
  function QueryInterface(const anID: TGUID; out anObj): hResult; virtual; 
  procedure SomeMethod;
 end;//TA
 TB = class(TA)
  function QueryInterface(const anID: TGUID; out anObj): hResult; override;
 end;//TB
 TC = class(TIntefacedObject, ISomeInterface)
  procedure SomeMethod;
 end;//TC
...
function TA._AddRef: Integer;
begin
 Result := -1;
end;
function TA._Release: Integer;
begin
 Result := -1;
end;
function TA.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if Self.GetInterface(anID, anObj) then
  Result := S_Ok
 else
  Result := E_NoInterface;
end;
procedure TA.SomeMethod;
begin
 Write('A');
end;
function TB.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if IsEqualGUID(anID, ISomeInterface) then
 begin
  Result := S_Ok;
  ISomeInterface(Obj) := TC.Create;
 end//IsEqualGUID(anID, ISomeInterface)
 else
  Result := inherited QueryInterface(anID, Obj);  
end;
procedure TC.SomeMethod;
begin
 Write('C');
end;
...
var
 l_A : ISomeInterface;
 l_B : ISomeInterface;
 A : TA;
 B : TB;
begin
 A := TA.Create;
 B := TB.Create;
 if not Supports(A, ISomeInterface, l_A) then
  Assert(false);
 l_A.SomeMethod; // - в консоли видим A
 if not Supports(B, ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - ТЕПЕРЬ в консоли видим C
end;
Мысль понятна?
Изменилась ОДНА строчка. А КАКОВА РАЗНИЦА!
Продолжим.
Напишем теперь так:
type
 ISomeFakeInterface = interface
 end;//ISomeFakeInterface
 ISomeInterface = interface
  procedure SomeMethod;
 end;//ISomeInterface
 TA = class(TObject, ISomeInterface {IUnknown тут СПЕЦИАЛЬНО опущен}, ISomeFakeInterface)
  function _AddRef: Integer;
  function _Release: Integer;
  function QueryInterface(const anID: TGUID; out anObj): hResult; virtual; 
  procedure SomeMethod;
 end;//TA
 TB = class(TA)
  function QueryInterface(const anID: TGUID; out anObj): hResult; override;
 end;//TB
 TC = class(TIntefacedObject, ISomeInterface)
  procedure SomeMethod;
 end;//TC
...
function TA._AddRef: Integer;
begin
 Result := -1;
end;
function TA._Release: Integer;
begin
 Result := -1;
end;
function TA.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if Self.GetInterface(anID, anObj) then
  Result := S_Ok
 else
  Result := E_NoInterface;
end;
procedure TA.SomeMethod;
begin
 Write('A');
end;
function TB.QueryInterface(const anID: TGUID; out anObj): hResult;
begin
 if IsEqualGUID(anID, ISomeInterface) then
 begin
  Result := S_Ok;
  ISomeInterface(Obj) := TC.Create;
 end//IsEqualGUID(anID, ISomeInterface)
 else
  Result := inherited QueryInterface(anID, Obj);  
end;
procedure TC.SomeMethod;
begin
 Write('C');
end;
...
var
 l_A : ISomeInterface;
 l_B : ISomeInterface;
 A : TA;
 B : TB;
begin
 A := TA.Create;
 B := TB.Create;
 if not Supports(A, ISomeInterface, l_A) then
  Assert(false);
 l_A.SomeMethod; // - в консоли видим A
 if not Supports(B, ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - в консоли видим A, а "хотелось бы" - C
 if not Supports(ISomeFakeInterface(B), ISomeInterface, l_B) then
  Assert(false);
 l_B.SomeMethod; // - в консоли видим C, ВУАЛЯ!!!
end;
УДИВИТЕЛЬНО!
Не правда ли?
НЕСИММЕТРИЯ метода Supports - ОЧЕВИДНА.
По-моему...
"Сухой остаток":
overload - "вреден", "вообще" и в ДАННОМ случае "в частности".
Особенно вреден overload с "ковариантными" типами.
Да и вообще говоря, можно было бы обойтись (на месте Borland'а) ТОЛЬКО методом Supports(IUnknown), а не "ГОРОДИТЬ" ещё один КРАЙНЕ НЕОЧЕВИДНЫЙ метод Supports(TObject).
Мысль понятна?
Сразу оговорюсь - "тема далеко не для всех".
Посему не бросайтесь комментировать, а сначала - "вкурите" проблему.
Надеюсь, что помог кому-то.
P.S. Если подобные "экзерсисы" с объектами vs. интерфейсы - ИНТЕРЕСНЫ - пишите. У меня "в кармане" ещё есть "запасец".
P.P.S. Ну и я надеюсь понятно, что GUID'ы в описании интерфейсов - ОПУЩЕНЫ. Их можно вставить по Crtl-Shift-G.
P.P.P.S. Ну и "в тему":
http://18delphi.blogspot.ru/2013/11/queryinterface.html
http://18delphi.blogspot.ru/2013/11/supports.html
http://18delphi.blogspot.ru/2013/10/supports.html
P.P.P.P.S Один из читателей любезно предоставил компилируемый пример - https://bitbucket.org/ingword/temp/src
 
stdcall там ещё конечно же забыл
ОтветитьУдалитьТо, что Вы здесь хотите:
ОтветитьУдалитьfunction TB.QueryInterface(const anID: TGUID; out anObj): hResult;
…
ISomeInterface(Obj) := TC.Create;
…
противоречит требованиям реализации QueryInterface:
https://docs.microsoft.com/en-us/windows/desktop/api/Unknwn/nf-unknwn-iunknown-queryinterface(q_)
"То, что Вы здесь хотите:"
ОтветитьУдалитьА что я хочу?