Когда у вас уже есть на руках ссылка на интерфейс, и вы хотите (попробовать) сконвертировать её в другой интерфейс, вы должны будете использовать какое-то преобразование или функцию преобразования. Равно как и с преобразованиями объект-к-интерфейсу, у вас есть несколько вариантов на выбор.
На выбор вам доступны жёсткие преобразования (hard-cast), преобразования as, проверки is, функция
Supports
и метод QueryInterface
. Как мы увидим ниже, не все эти методы доступны в равной мере на Win32 и .NET, а некоторые доступные методы имеют различия в поведении на разных платформах.Преобразование через as
В Win32, если вы попробуете применить as для интерфейса, который не поддерживается, то вы получите исключение. В Delphi 8 for .NET вместо этого as возвращаетnil
. Это баг, который был исправлен в Delphi 2005.Жёсткое приведение типа
Компилятор позволяет вам использовать жёсткое приведение типов для приведения интерфейса к другому интерфейсу как в .NET, так и в Win32. Однако только .NET вариант является безопасным и делает то, что вы ожидаете (возвращаяnil
в случае неудачи преобразования). В Win32 это преобразование не будет работать, потому что оно говорит компилятору трактовать ссылку на интерфейс, как если бы это уже была ссылка на интерфейс другого типа. Обычно это преобразование не имеет ценности в Win32.is-проверки
По какой-то причине в Win32 проверки is не поддерживаются для интерфейсов. Чтобы проверить, что ссылка на интерфейс реализует другой интерфейс, вам нужно использовать функциюSupports
или явно вызывать метод QueryInterface
. .NET же поддерживает is-проверки для интерфейсных ссылок.Функция Supports
Обе платформы Win32 и .NET поддерживают использование Supports
для проверки того, что интерфейсная ссылка поддерживает другой интерфейс и одновременно для возврата ссылки на этот интерфейс. В .NET функция Supports
имеет те же самые особенности, что и в случае преобразования объекта к интерфейсу (фактически, это в точности та же самая функция - любая интерфейсная ссылка совместима с TObject
); т.е. она относительно медленная.Метод QueryInterface
Этот способ применим только к Win32, где все интерфейсы имеют метод QueryInterface
, унаследованный от базового интерфейса IInterface
(или IUnknown
). Под капотом языка функция Supports
(и даже операции is и as) вызывают QueryInterface
. Конечно же, ничто не останавливает вас от прямого вызова QueryInterface
, но тогда вы привязываете себя к Win32, потому что этот код не будет работать в .NET, и его нужно будет изменить.Пример кода
Давайте напишем небольшой пример, который демонстрирует различные способы приведения и проверок между двумя интерфейсными типами:program TestIntf2Inf; {$APPTYPE CONSOLE} uses SysUtils; type IMyInterface = interface ['{99D91C44-BCE7-4D35-A661-DE32E8AE56FC}'] procedure Foo; end; IMyInterface2 = interface ['{2E200094-0643-46C8-87AF-AAB0A1F5801D}'] procedure Bar; end; INotImplemented= interface ['{BAEE6F63-FF47-4877-9657-443B6D1555FA}'] procedure Zoo; end; TMyObject = class(TInterfacedObject, IMyInterface, IMyInterface2) procedure Foo; procedure Bar; end; procedure TMyObject.Foo; begin WriteLn(ClassName, '.Foo!'); end; procedure TMyObject.Bar; begin WriteLn(ClassName, '.Bar'); end; procedure Foo(const I: IMyInterface); var MyInterface2: IMyInterface2; NotImplemented: INotImplemented; begin // Win32 поддерживает as и Supports. // Hard-cast небезопасен, он не сработает // .NET поддерживает as, Supports, Hard-cast и is MyInterface2 := I as IMyInterface2; MyInterface2.Bar; // hard-cast в .NET: безопасен, возвращает nil при ошибке // hard-cast в Win32: компилируется, но не работает MyInterface2 := IMyInterface2(I); // .NET: Вызывает TMyObject.Bar // .Win32: Вызывает TMyObject.Foo! MyInterface2.Bar; try NotImplemented := I as INotImplemented; if not Assigned(NotImplemented) then WriteLn('D8 .NET bug: as returns nil'+ ' - should raise exception'); NotImplemented.Zoo; except {$IFDEF Win32} on E: EIntfCastError do {$ENDIF} {$IFDEF CLR} on E: InvalidCastException do {$ENDIF} WriteLn('As designed: ', E.ClassName, E.Message); on E: Exception do WriteLn('Bug: ', E.ClassName, E.Message); end; // Supports работает и в Win32 и в .NET if Supports(I, IMyInterface2, MyInterface2) then MyInterface2.Bar; {$IFDEF CLR} // "intf is intf" не поддерживается в Win32 if I is IMyInterface2 then WriteLn('interface is interface suppported in .NET'); {$ENDIF} {$IFDEF Win32} // QueryInterface не поддерживается в .NET if I.QueryInterface(IMyInterface2, MyInterface2) = 0 then MyInterface2.Bar; WriteLn('QueryInterface supported in Win32'); {$ENDIF} end; var I : IMyInterface; begin I := TMyObject.Create; try Foo(I); except on E: Exception do WriteLn(E.Message); end; ReadLn; end.Этот код должен компилироваться и работать в D7, D8.NET, D2005 Win32 и D2005 .NET. Вот его вывод для каждого случая:
Вывод от Delphi 2005 Win32 и D7:
TMyObject.Bar TMyObject.Foo! As designed: EIntfCastErrorInterface not supported TMyObject.Bar TMyObject.Bar QueryInterface supported in Win32
Вывод от Delphi 8 .NET:
TMyObject.Bar TMyObject.Bar D8 .NET bug: as returns nil - should raise exception Bug: NullReferenceExceptionObject reference not set to an instance of an object. TMyObject.Bar interface is interface suppported in .NET
Вывод от Delphi 2005 .NET:
TMyObject.Bar TMyObject.Bar As designed: InvalidCastExceptionSpecified cast is not valid. TMyObject.Bar interface is interface suppported in .NET
Обратите внимание, что баг D8 в приведении as был исправлен в D2005 и что жёсткие приведения типов в Win32 имеют странные эффекты вроде вызова неверного метода! Это не баг, а следствие семантики жёсткого приведения типов в Win32.
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.