Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2005.10.30;
Скачать: [xml.tar.bz2];

Вниз

Вопрос про интерфейсы   Найти похожие ветки 

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

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

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?
Хотя вроде должен...


 
Digitman ©   (2005-10-07 13:20) [1]

потому что IInterface2 - наследник IInterface1

TObject2 в дан.случае поддерживает и реализует оба интерфейса


 
Суслик ©   (2005-10-07 14:07) [2]


> TObject2 в дан.случае поддерживает и реализует оба интерфейса

Неверный ответ.

Поддерживает он только второй - см. GetInterfaceTable. О первом интерфейсе (IInterface1) класс вообще не знает.

Ты не сможешь сделать I as IInterface1 - будет interface not supported.

Судя по CPU компилятор лезет вызывать методы, предполагая, что они там есть. И более того вызывает их правильно. При этом никаких кастингов вообще не делает.


 
Суслик ©   (2005-10-07 14:10) [3]

Извини, Сергей.

С


> Неверный ответ.


я скорее всего поторопился. Скорее - неполный ответ.

Вот смотри - с одной стороны класс поддерживающий IInterface2 подставляется в место, где трубется IInterface1. Но с другой стороны этот же класс не поддерживает IInterface1 (т.е. к нему нельзя сделать каст).

Как-то нелогично, я прошу объяснить смысл нелогичности.


 
Digitman ©   (2005-10-07 14:13) [4]


> Неверный ответ.


да ты что ?!) ... неужели ?)


> О первом интерфейсе (IInterface1) класс вообще не знает


здрасть !

а объявление и реализация процедуры Do1() в этом классе - это для Пушкина ?)

раз не знает, так убери попробуй !
зачем оно тебе, раз "не знает" ?)


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


>  [4] Digitman ©   (07.10.05 14:13)


Используй GetInterfaceTable и увидишь, что класс знает только о IInterface2.
Поэтому невозможно сделать каст I as IInterface1. Попробуй.

Почему так? Почему I принимается в параметрах, где нужен IInterface1, но каст к IInterface1 невозможен?

Нелогично как-то.


 
Суслик ©   (2005-10-07 14:18) [6]

Пардон, так верно

> Используй GetInterfaceTable и увидишь, что класс знает только
> о IInterface1.


 
Digitman ©   (2005-10-07 14:21) [7]


> Но с другой стороны этот же класс не поддерживает IInterface1
> (т.е. к нему нельзя сделать каст).


да поддерживает же !

что ты говоришь-то ?

и как же незьзя сделать каст, кода можно и причем - запросто ?

вот смотри, как явно это делается ("грязная работа"):

var
 I1: IInterface1;
 I2: IInterface2;

 I2 := TObject2.create();
 I1 := IInterface1(IUnknown(I2)); //можно и напрямую - IInterface1(I2), ничто в дан.случае этому не противоречит
 UseInterface1(I1); //и поехали !

в твоем же случае, т.е.

var
 I: IInterface2;
..
 I := TObject2.create();
 UseInterface1(I);

эту "грязную" работу неявно делает компилятор, ибо он достаточно "умный" и понимает, что ты требуешь от него вполне допустимое и реализуемое приведение типов ...


 
Суслик ©   (2005-10-07 14:26) [8]


> да поддерживает же !


тогда почему as не работает?


 
isasa ©   (2005-10-07 14:33) [9]

в явно???

TObject2 = class(TInterfacedObject, IInterface2, IInterface1)


 
Суслик ©   (2005-10-07 14:33) [10]

Я с тобой в общем-то согласен. Я просто хочу понять, почему на мой взгляд все как-то нелогично. Почему as не работает? Почему явное приведение типов правильно? И


 
Суслик ©   (2005-10-07 14:47) [11]


> [7] Digitman ©   (07.10.05 14:21)

Я так понимаю, что в полиформизме классов и интерфейсов есть большая разница. Я что-то никак не могу ее ухватить :(


 
Digitman ©   (2005-10-07 14:51) [12]


> почему as не работает?


потому что AS приведет к вызову метода TInterfacedObject.QueryInterface() ... который в конечном итоге обратится к TObject.GetInterface, который в свою очередь обратится к GetInterfaceTable для получения таблицы интерфейсов, в которой, как ты правильно заметил, фигурирует лишь IInterface2

в случае же UseInterface1(I) обращение к InterfaceTable вообще не происходит


 
Суслик ©   (2005-10-07 14:52) [13]

Я вот и не могу понять концептуальную разницу в наслеловании интерфейсов и классов. Вернее интерфейсов классов и интерфейсов интерфейсов.


 
Digitman ©   (2005-10-07 14:54) [14]


> почему as не работает?


потому что AS приведет к вызову метода TInterfacedObject.QueryInterface() ... который в конечном итоге обратится к TObject.GetInterface, который в свою очередь обратится к GetInterfaceTable для получения таблицы интерфейсов, в которой, как ты правильно заметил, фигурирует лишь IInterface2

в случае же UseInterface1(I) обращение к InterfaceTable вообще не происходит


 
Digitman ©   (2005-10-07 15:01) [15]

но разницу в поведении программы в случае

UseInterface1(I)

и

UseInterface1(I AS Interface1)

ты, надеюсь, понимаешь ?


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


> UseInterface1(I)

здесь передается IInterface2


> UseInterface1(I AS Interface1)

здесь передается IInterface1


 
Digitman ©   (2005-10-07 15:31) [17]


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


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


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


> ты в окно 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 16:22) [19]


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


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

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

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


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


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

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

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

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


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


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


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

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

type
 IFoo = interface(IBar)

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

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


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


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


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


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

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

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

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

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


 
Digitman ©   (2005-10-07 16:50) [22]


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


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


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


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

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

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


 
Digitman ©   (2005-10-07 17:08) [24]


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


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


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


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


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


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

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

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

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


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

> Суслик ©

Дело в том, что интерфейсы базируются на наследовании декларации. Написав 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 18:32) [27]

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


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


>  [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-10 16:43) [29]


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

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

procedure IInterface1.Do1 = do3

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


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


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

Да не за что.

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


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


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


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


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


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


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


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

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


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


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


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

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


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


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


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

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

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


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


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

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



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2005.10.30;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.56 MB
Время: 0.041 c
3-1127299186
Juice
2005-09-21 14:39
2005.10.30
Lookup-поле в сетке


1-1128523688
X9
2005-10-05 18:48
2005.10.30
Работа с TXMLDocument и IXMLNode


2-1128805510
The Sound
2005-10-09 01:05
2005.10.30
Ошибка SMTP.


5-1107459735
Сергей Д.
2005-02-03 22:42
2005.10.30
TPanel с вертикальным текстом


2-1128563154
Viktop
2005-10-06 05:45
2005.10.30
Формат файла для теста





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский