Есть несколько способов проверить, реализует ли объект данный интерфейс или нет - но на разных платформах есть небольшие различия.
Преобразование as работает (практически) одинаково в Win32 и .NET.
var MyObject: TMyObject; MyInterface: IMyInterface; begin // ... MyInterface := MyObject as IMyInterface; // может возбудить EInvalidCast MyInterface.Foo; end;Отличия платформ в том, что на Win32 объявленный тип объекта должен реализовывать интерфейс
IInterface (или IUnknown), а в .NET подойдёт любой тип объекта. Если преобразование неудачно - возбуждается исключение. Иногда это не то, что вам нужно. На .NET вы можете привести тип от любого объекта к любому интерфейсу, используя синтаксис жёсткого приведения - и это будет работать. Если преобразование будет неудачным, то просто вернётся nil:
var MyObject: TMyObject; MyInterface: IMyInterface; begin ... MyInterface := IMyInterface(MyObject); // специфично для .NET if Assigned(MyInterface) then MyInterface.Foo; end;В настоящий момент компилятор Win32 не поддерживает такое преобразование в общем случае. Он поддерживает его только в особом случае, когда преобразование может быть проверено на этапе компиляции. Это может быть, когда вы объявляете объект типа, который поддерживает данный интерфейс. Если это не так, то компилятор выдаст вам такую ошибку:
[Error]: Incompatible types: 'IMyInterface' and 'TInterfacedObject'Чтобы проверить, поддерживает ли объект интерфейс без возбуждения исключений, вы должны сначала преобразовать его к
IInterface, а затем вызвать метод QueryInterface. Эта возможность существует только в Win32:
Intf := IInterface(MyObject); if Intf.QueryInterface(IMyInterface, MyInteface) = 0 then MyInterface.Foo;Функция
Supports в модуле SysUtils даёт вам более удобный (и кросс-платформенный) способ для такой проверки:
if Supports(MyObject,IMyInterface, MyInteface) then MyInterface.Foo;
Supports также доступна в .NET, но её реализация делает её намного медленнее, чем использование жёсткого преобразования, специфичного для .NET. Это потому что .NET-вариант Supports реализован в терминах новой конструкции 'type of interface':
type TInterfaceRef = type of interface; ... function Supports(const Instance: TObject; const IID: TInterfaceRef; out Intf): Boolean; begin Result := Instance is IID; if Result then Intf := Instance as IID; end;Конструкция 'type of interface', вообще-то, довольно изящна. Она позволяет вам передавать любой интерфейсный тип параметром в метод. Это используется вместо передачи
TGUID как идентификатора или описателя данного интерфейса в Win32. Функция Supports компилируется в такой IL-код:
.method public hidebysig static bool Supports(object Instance, [mscorlib]System.RuntimeTypeHandle IID, object& Intf) cil managed 
{ 
   // Code Size: 50 byte(s) 
   .maxstack 3 
   .locals ( 
      bool flag1, 
      [mscorlib]System.Type type1) 
   L_0000: ldarg.1 
   L_0001: call [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle([mscorlib]System.RuntimeTypeHandle) 
   L_0006: ldarg.0 
   L_0007: callvirt instance bool [mscorlib]System.Type::IsInstanceOfType(object) 
   L_000c: stloc.0 
   L_000d: ldloc.0 
   L_000e: brfalse.s L_0030 
   L_0010: ldarg.2 
   L_0011: ldarg.1 
   L_0012: call [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle([mscorlib]System.RuntimeTypeHandle) 
   L_0017: stloc.1 
   L_0018: ldloc.1 
   L_0019: ldarg.0 
   L_001a: callvirt instance bool [mscorlib]System.Type::IsInstanceOfType(object) 
   L_001f: brtrue.s L_0029 
   L_0021: ldarg.0 
   L_0022: ldloc.1 
   L_0023: call object Borland.Delphi.Units.System::@CreateCastException(object, [mscorlib]System.Type) 
   L_0028: throw 
   L_0029: ldarg.0 
   L_002a: castclass object 
   L_002f: stind.ref 
   L_0030: ldloc.0 
   L_0031: ret 
}
Довольно много кода!С небольшой помощью
Reflector-а, я обнаружил, что этот код примерно соответствует такому Delphi-коду:
function Supports(Instance: TObject; IID: RuntimeTypeHandle; out Intf: TObject): boolean; 
var 
  InterfaceType: System.Type; 
begin 
  Result := System.Type.GetTypeFromHandle(IID).IsInstanceOfType(Instance); 
  if Result then 
  begin 
    InterfaceType := System.Type.GetTypeFromHandle(IID); 
    if (not InterfaceType.IsInstanceOfType(Instance)) then 
     raise EInvalidCast.Create(SInvalidCast); 
    Intf := Instance; 
  end; 
end;
Заметьте, что этот код немного избыточен. Наличие обеих конструкций is и as заставляет компилятор вызывать GetTypeFromHandle и IsInstanceOfType дважды. Исключение в этом методе никогда не вызывается. Вызов Supports генерирует такой IL-код:
ldloc.1 ldtoken Obj2IntfCastNET.IMyInterface call [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle([mscorlib]System.RuntimeTypeHandle) pop ldtoken Obj2IntfCastNET.IMyInterface ldloca.s obj2 call bool Borland.Vcl.Units.SysUtils::Supports(object, [mscorlib]System.RuntimeTypeHandle, object&) stloc.0Сравните это с жёстким преобразованием объект-в-интерфейс, который даёт вам этот простой и эффективный IL-код:
ldloc.s obj2 isinst Obj2IntfCastNET.IMyInterface stloc.2Угадаете, что работает быстрее?
Так что вы можете использовать кросс-платформенную функцию
Supports (которая достаточно эффективна в Win32, но относительно медленная в .NET), чтобы сделать свой код одинаковым на всех платформах. Или же вы можете писать код для каждой платформы отдельно, и тогда вы можете воспользоваться эффективным на .NET жёстким приведением типа. В большинстве случаев, эти накладные расходы для .NET-варианта Supports не имеют значения, но для каких-то критичных к времени выполнения кусков вы можете захотеть выбрать самое эффективное решение для этой платформы, например:
{$IFDEF WIN32} 
  if Supports(MyObject,IMyInterface, MyInterface) then 
{$ENDIF} 
{$IFDEF CLR} 
  MyInterface := IMyInterface(MyObject); 
  if Assigned(MyInterface) then 
{$ENDIF} 
  MyInterface.Foo;
Я надеюсь, что в будущих версиях компилятора Win32 можно будет делать жёсткое приведение типов от объекта к интерфейсу так же, как сейчас это происходит на .NET. Преобразование должно возвращать nil, если объект не поддерживает интерфейс (да, это предложение было отправлено Borland-у). В .NET вы также можете использовать оператор 'is', чтобы проверить, реализует ли объект интерфейс или нет. Это не поддерживается в Win32.
if MyObject is IMyInterface then 
  Writeln('Yup'); 
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.