Ваш код содержит два неверных предположения:
- что вы можете получить значимые
RTTI
из интерфейсов. Ой, вы можете получить RTTI из интерфейса типов .
- То, что Интерфейс всегда реализуется объектом Delphi (следовательно, вы пытаетесь извлечь RTTI из вспомогательного объекта Delphi).
Оба предположения неверны. Интерфейсы - это очень простые таблицы VIRTUAL METHOD, очень мало волшебства для них. Поскольку интерфейс так узко определен, он не может иметь RTTI
. Если, конечно, вы не реализуете свой собственный вариант RTTI
, и вы не должны этого делать. LE: Сам интерфейс не может передавать информацию о типе так, как это делает объект TObject, но оператор TypeOf () может получить ВведитеInfo, если предоставляется IInterface
Ваше второе предположение также неверно, но не так. В мире Delphi большинство интерфейсов будут реализованы объектами Delphi, если, конечно, вы не получите интерфейс из DLL, написанной на другом языке программирования: интерфейсы Delphi совместимы с COM, поэтому его реализации можно использовать из любого другого COM-совместимого языка и наоборот. Но поскольку мы говорим здесь о Delphi XE, вы можете использовать этот синтаксис для приведения интерфейса к реализующему его объекту интуитивно понятным и читабельным способом:
TObject := IInterface as TObject;
, то есть используйте оператор as
. Delphi XE время от времени автоматически конвертирует жесткое приведение этого типа:
TObject := TObject(IInterface);
к упомянутому "as"
синтаксису, но мне не нравится это волшебство, потому что оно выглядит очень нелогичным и ведет себя по-разному в старых версиях Delphi.
Приведение Interface
обратно к его реализующему объекту также неверно с другой точки зрения: он будет показывать all свойства реализующего объекта, а не только те, которые связаны с интерфейсом, и это очень неправильно потому что вы используете интерфейсы, чтобы скрыть эти детали реализации в первую очередь!
Пример: реализация интерфейса не поддерживается объектом Delphi
Просто для удовольствия, вот небольшая демонстрация интерфейса, который не , поддерживаемый объектом Delphi. Поскольку интерфейс - это не что иное, как указатель на таблицу виртуальных методов, я создам таблицу виртуальных методов, создам указатель на нее и приведу указатель на нужный тип интерфейса. Все указатели методов в моей поддельной таблице виртуальных методов реализованы с использованием глобальных функций и процедур. Только представьте, что вы пытаетесь извлечь RTTI из моего i2
интерфейса!
program Project26;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
// This is the interface I will implement without using TObject
ITestInterface = interface
['{CFC4942D-D8A3-4C81-BB5C-6127B569433A}']
procedure WriteYourName;
end;
// This is a sample, sane implementation of the interface using an
// TInterfacedObject method
TSaneImplementation = class(TInterfacedObject, ITestInterface)
public
procedure WriteYourName;
end;
// I'll use this record to construct the Virtual Method Table. I could use a simple
// array, but selected to use the record to make it easier to see. In other words,
// the record is only used for grouping.
TAbnormalImplementation_VMT = record
QueryInterface: Pointer;
AddRef: Pointer;
ReleaseRef: Pointer;
WriteYourName: Pointer;
end;
// This is the object-based implementation of WriteYourName
procedure TSaneImplementation.WriteYourName;
begin
Writeln('I am the sane interface implementation');
end;
// This will implement QueryInterfce for my fake IInterface implementation. All the code does
// is say the requested interface is not supported!
function FakeQueryInterface(const Self:Pointer; const IID: TGUID; out Obj): HResult; stdcall;
begin
Result := S_FALSE;
end;
// This will handle reference counting for my interface. I am not using true reference counting
// since there is no memory to be freed, si I am simply returning -1
function DummyRefCounting(const Self:Pointer): Integer; stdcall;
begin
Result := -1;
end;
// This is the implementation of WriteYourName for my fake interface.
procedure FakeWriteYourName(const Self:Pointer);
begin
WriteLn('I am the very FAKE interface implementation');
end;
var i1, i2: ITestInterface;
R: TAbnormalImplementation_VMT;
PR: Pointer;
begin
// Instantiate the sane implementation
i1 := TSaneImplementation.Create;
// Instantiate the very wrong implementation
R.QueryInterface := @FakeQueryInterface;
R.AddRef := @DummyRefCounting;
R.ReleaseRef := @DummyRefCounting;
R.WriteYourName := @FakeWriteYourName;
PR := @R;
i2 := ITestInterface(@PR);
// As far as all the code using ITestInterface is concerned, there is no difference
// between "i1" and "i2": they are just two interface implementations.
i1.WriteYourName; // Calls the sane implementation
i2.WriteYourName; // Calls my special implementation of the interface
WriteLn('Press ENTER to EXIT');
ReadLn;
end.