Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Вопрос про интерфейсы


Страницы:   1 2

Суслик ©   (2005-10-07 13:09:00) 

Почему этот код

type
  IInterface1 = interface
     ['{E7550A1F-FE4B-410E-8EA0-E9882534AAE8}']
     procedure Do1();
  end;

  IInterface2 = interface(IInterface1)
     ['{FA7F65BF-93DC-4491-AC41-1D8970CCD649}']
     procedure Do2();
  end;

  TObject2 = class(TInterfacedObject, IInterface2)
     procedure Do1();
     procedure Do2();
  end;

procedure TObject2.Do1();
begin
  ShowMessage('1');
end;

procedure TObject2.Do2();
begin
  ShowMessage('2');
end;

procedure UseInterface1(I: IInterface1);
begin
  I.Do1();
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  I: IInterface2;
begin
  I := TObject2.create();
  UseInterface1(I);
end;


На строчке UseInterface1(I) не дает ошибку Interface not supported?
Хотя вроде должен...


Суслик   (2005-10-10 19:30:00) [1]


> некоторый теоретический интерес, но никак не практический

вот именно. Ничего более.


Набережных С.   (2005-10-10 19:27:00) [2]


> > ничто не мешает разместить ссылку на него в таблице интерфейса.
>
>
> кроме того, что мест для этого метода может быть несколько.
>  (см. выше пример с 3мя интерфейсами)


> Суслик ©   (10.10.05 18:50) [34]

Ну и что? Чем это мешает?

Вообще говоря, для программиста имеет значение только одно - что реализация поддержки интерфейсов в Delphi полностью соответствует требованиям COM. И только из этого следует исходить, и только на это рассчитывать. А то, как конкретно это реализовано, может представлять некоторый теоретический интерес, но никак не практический. Сейчас реализация такая, а завтра может быть другая или третья. Вон, в Demos, например, есть пример реализации вообще без объектов. Ну и что? Важно только то, что в обоих случаях поведение интерфейсной ссылки одинаково, остальное лирика. Такое вот мое мнение.


Суслик   (2005-10-10 18:50:00) [3]


>  [32] Набережных С. ©   (10.10.05 18:10)


> ничто не мешает разместить ссылку на него в таблице интерфейса.

кроме того, что мест для этого метода может быть несколько. (см. выше пример с 3мя интерфейсами)


Бурундук   (2005-10-10 18:23:00) [4]

В таблице интерфейса помещаются не указатели на сами
функции, а переходники, корректирующие Self.
В зависимости от того, реализует ли метод интерфейса
статический метод объекта или виртуальный,
код этого переходника будет отличаться.
Других отличий, кажется, нет.


Набережных С.   (2005-10-10 18:10:00) [5]


> Суслик ©   (10.10.05 16:55) [30]
> как устроен объект, в котором есть виртуальные и АБСТАКТНЫЕ
> методы


С этим, как я понимаю, все просто. Абрастктным-то объявляется метод ОБЪЕКТА, у интерфеса все методы абстрактные в полном смысле этого слова:) А абсрактный метод объекта, как уже сказал Игорь, это вполне конкретный метод, и ничто не мешает разместить ссылку на него в таблице интерфейса.


Игорь Шевченко   (2005-10-10 17:27:00) [6]


> Я тут когда разберусь (сейчас времени нет) как устроен объект,
>  в котором есть виртуальные и АБСТАКТНЫЕ методы, реализующие
> интерфейсные фукнции, запостю сюда. Тоже может быть кому-
> то интересно.


Я не совсем понимаю, что мешает методу реализации интерфейса быть абстрактным ? Механизм абстракции есть ссылка на процедуру AbstractError, то есть, метод реализуется в любом случае


Суслик   (2005-10-10 16:55:00) [7]


>  [29] Набережных С. ©   (10.10.05 16:43)

Да не за что.

Я тут когда разберусь (сейчас времени нет) как устроен объект, в котором есть виртуальные и АБСТАКТНЫЕ методы, реализующие интерфейсные фукнции, запостю сюда. Тоже может быть кому-то интересно.


Набережных С.   (2005-10-10 16:43:00) [8]


> Суслик ©   (10.10.05 13:06) [28]

Хм, точно! Видимо, когда я с этим разбирался, у меня была ситуация типа

procedure IInterface1.Do1 = do3

а я решил, что оно всегда так, ан нет, оказывается:)
Спасибо!


Суслик   (2005-10-10 13:06:00) [9]


>  [27] Набережных С. ©   (07.10.05 18:32)

Ну в общем я порял все так:
согласно информации, полученной из cpu, в памяти начало таблицы IInterface1 и IInterface2 находятся по одному адресу. Просто первая часть (до метода Do1 включительно) соответствует IInterface1, а первая часть ПЛЮС метод Do2 соответствует интерфейсу IInterface2.

ЗЫ. Я говорю про код

IInterface1 = interface
   ['{E7550A1F-FE4B-410E-8EA0-E9882534AAE8}']
   procedure Do1();
end;

IInterface2 = interface(IInterface1)
   ['{FA7F65BF-93DC-4491-AC41-1D8970CCD649}']
   procedure Do2();
end;

TObject2 = class(TInterfacedObject, IInterface1, IInterface2)
   procedure Do1();
   procedure Do2();
end;


Набережных С.   (2005-10-07 18:32:00) [10]

Извиняюсь, очепятка:) Цитату "а таблица IInterface2 - методы AddRef, Release, Do1 и Do2" следует заменить на "а таблица IInterface2 - методы QueryInterface, AddRef, Release, Do1 и Do2"


Набережных С.   (2005-10-07 18:27:00) [11]

> Суслик ©

Дело в том, что интерфейсы базируются на наследовании декларации. Написав IInterface2 = interface(IInterface1) ты объявил, что IInterface2 включает в себя все методы, объявленные в интерфейсе IInterface1 плюс те, что объявлены в самом IInterface2. В некотором смысле объявление
IInterface1 = interface
    ['{E7550A1F-FE4B-410E-8EA0-E9882534AAE8}']
    procedure Do1();
 end;

 IInterface2 = interface(IInterface1)
    ['{FA7F65BF-93DC-4491-AC41-1D8970CCD649}']
    procedure Do2();
 end;

идентично объявлению
 IInterface2 = interface
    ['{FA7F65BF-93DC-4491-AC41-1D8970CCD649}']
    procedure Do1();
    procedure Do2();

Т.е. таблица методов, которую представляет собой IInterface2, в обеих случаях будет одинаковой. Хотя, разумеется, семантически это разные объявления. Это можно продемонстрировать так:
type
 IInterface1 = interface
    ['{E7550A1F-FE4B-410E-8EA0-E9882534AAE8}']
    procedure Do1();
 end;

 IInterface2 = interface(IInterface1)
    ['{FA7F65BF-93DC-4491-AC41-1D8970CCD649}']
    procedure Do2();
 end;

 TObject2 = class(TInterfacedObject, IInterface1, IInterface2)
    procedure I1Do1();
    procedure Do1();
    procedure Do2();
    procedure Interface1.Do1 = I1Do1;
 end;


Увидешь, что вызывается метод того интерфейса, который явно был запрошен, несмотря на приведение типов - подобно виртуальным методам.
Это показывает, что в объекте существуют две таблицы методов, для каждого интерфейса, причем таблица IInterface1 содержит методы  QueryInterface, AddRef, Release и Do1, а таблица IInterface2 - методы AddRef, Release, Do1 и Do2.

Что до реализации, то  действительно, дельфийский объект поддерживает только те интерфейсы, которые явно объявлены в нем самом, либо в предках этого объекта и только такой интерфейс может быть получен через вызов GetInterface. А оператор AS реально компилируется в вызов QuieryInterface и генерацию исключения при отрицательном результате.


Суслик   (2005-10-07 17:52:00) [12]

я тут с удивлением узнал, что методы реализующие методы интерфейса можно делать виртуальными и АБСТРАКТЫМИ.

Первое я знал. Но второе меня пока поразило.

Надо будет дома посмотреть как в этом случае vmt и соотв. таблица для интерфейсов реализована.


Digitman   (2005-10-07 17:08:00) [13]


> Суслик ©   (07.10.05 17:01) [23]


> в этом случае можно QuieryInterface пользоваться


не только "можно", но и НУЖНО !
а иначе - никак.


> нужно хорошо знать семантику as и проблем не будет


Борланд как всегда полон тайн)
Но пустив нас в свою кухню хотя бы черед то же самое "Окно CPU" он не имеет ничего против исследования нами своей "кухни" ... было бы желание) ..


Суслик   (2005-10-07 17:01:00) [14]

Ну в этом случае можно QuieryInterface пользоваться.

На самом деле согласен. Вцелом просто нужно хорошо знать семантику as и проблем не будет. Ну буду знать - спасибо.


Digitman   (2005-10-07 16:50:00) [15]


> Суслик ©   (07.10.05 16:35) [21]


> Будь я на месте разработчика


понимаешь ли, AS - подход универсальный, для любых интерфейсных объектов, будь они прямо в том же Делфи-приложении объявлены и реализованы либо возвращенных системным OLE/СОМ-механизмом, обратившимся к фабрике класса возможно в совершенно ином приложении ... в последнем случае тот самый "compiler magic" бессилен, ибо речь идет о прокси, НЕ знающем ни о каких дельфийских "выкрутасах" с приведениями типов.


Суслик   (2005-10-07 16:35:00) [16]


>  [20] Игорь Шевченко ©   (07.10.05 16:23)


> А нафига ж тогда, спрашивается, наследование ?


Говорит. Я до некоторой поры нисколько в этом не сомневался.

Но потом как-то наткнулся на то, что as работает не так как для классов. И по аналогии с классами решил (не особо проверяя, т.к. почти не пользовался тогда интерфейсами), что т.к. as для классов это всего-лишь безопасное приведение типов, то as для интерфейсов должно обладать теми же особенностями, что и для опасное приведение типов для интерфейсов.

В общем не очень понятно выразился. Виноват.

В общем подводя итог топику сказал бы что - мне смутила семантика as для интерфейсов. ВСЕ остальное меня вполне удовлетворяет.

Будь я на месте разработчика я бы сделал не так: не делал бы вообще as для интерфейсов - пусть явно QueryInterface программисты вызывают. Тогда бы и вопросов было меньше.


Игорь Шевченко   (2005-10-07 16:23:00) [17]


> А это поведение является документированным, т.е. что реализация
> именно такая? Т.е. когда ты по адресу одного интерфейса
> можешь вызывать все методы интерфейса предка?


Я эта...извиняюсь....

а что, запись вида

type
 IFoo = interface(IBar)

уже нифига не говорит об этой возможности ?

А нафига ж тогда, спрашивается, наследование ?


Digitman   (2005-10-07 16:22:00) [18]


> это поведение является документированным, т.е. что реализация
> именно такая?


до сей поры у меня не было никаких сомнений на сей счет.

подобный код как работал под Д5, так и работает в Д7 без малейших нареканий

Д6 не рассматривал.


> по адресу одного интерфейса можешь вызывать все методы интерфейса
> предка


у интерфейса нет ни адреса как такового ни самого понятия "адрес"

адрес есть у объекта, представляющего и реализующего интерфейс.

пока речь идет о внутреннем преобразовании типовкомпилятором Делфи (compiler magic), заботиться о совместимости версий нет повода

впрочем. нет повода об этом заботиться и в ином случае,  когда требуется явныйзапрос интерфейса, как в случае с AS - любой объект, представляющий интерфейс IUnknown, обязан тем илим иным образом реализовать метод QueryInterface, в теле которого он, право, волен делать все что угодно, в .т.ч и искать-возвращать любой из "своих" или "чужих" интерфейсов


Суслик   (2005-10-07 16:11:00) [19]


> ты в окно CPU заглядывал ?

Посмотрел.

Да. Хитро у них сделано. Не спорю. Все дело в IOffset из TInterfaceEntry.

Особенно интересная картина получается, вот в таком случае


type
  IInterface1 = interface
     ['{E7550A1F-FE4B-410E-8EA0-E9882534AAE8}']
     procedure Do1();
  end;

  IInterface2 = interface(IInterface1)
     ['{FA7F65BF-93DC-4491-AC41-1D8970CCD649}']
     procedure Do2();
  end;

  IInterface3 = interface(IInterface1)
     ['{DBFC913F-1CC3-4A24-B248-1D684EF8C5E7}']
     procedure Do3();
  end;

  TObject2 = class(TInterfacedObject, IInterface1, IInterface2, IInterface3)
     procedure Do1();
     procedure Do2();
     procedure Do3();
  end;


Т.е. они где могут хрянят таблицы методов интерфейсов предка и потомка по одним адресам. Если такое сделать нельзя (как вприведенном примере), то методы дублируются.

Т.е. как бы хранятся ( в данном пример):

Do1 - для IInterface1 + IInterface3
Do3  - для IInterface3
Do1  - для IInterface2
Do2 - для IInterface2

Тогда такой вопрос. А это поведение является документированным, т.е. что реализация именно такая? Т.е. когда ты по адресу одного интерфейса можешь вызывать все методы интерфейса предка?


Digitman   (2005-10-07 15:31:00) [20]


> Суслик ©   (07.10.05 15:19) [16]


ты в окно CPU заглядывал ?


Страницы:   1 2

Наверх

Титульная страница Поиск, карта сайта Написать письмо