Использование функции Supports() с общим типом интерфейса

Я просто попробовал свое первое использование дженериков в Delphi 2009 и недоумеваю, как использовать общий тип в качестве входа в функцию Supports, используемую для проверки того, реализует ли объект данный интерфейс. Я создал небольшой образец, иллюстрирующий проблему.

Учитывая следующие типы и функцию полезности:

IMyInterface = interface
['{60F37191-5B95-45BC-8C14-76633826889E}']
end;

TMyObject = class(TInterfacedObject, IMyInterface)
end;

class function TFunctions.GetInterface<T>(myObject: TObject): T;
var
  specificInterface: T;
begin
  // This would compile, but looses the generic capability
  //Supports(myObject, IMyInterface, specificInterface);

  // This results in compile errors
  Supports(myObject, T, specificInterface);

  result := specificInterface;
end;

и следующий фрагмент кода:

class procedure TFunctions.Test;
var
  myObject: TMyObject;
  myInterface: IMyInterface;
begin
  myObject := TMyObject.Create;

  myInterface := GetInterface<IMyInterface>(myObject);
end;

Я не ожидал бы никаких проблем, но получаю следующие ошибки времени компиляции:

[Ошибка DCC] GenericExample.pas(37): E2029 '(' expected, но ',' found [DCC Error] GenericExample.pas(37): E2014 Ожидаемое утверждение, но выражение типа 'T' найдено

Я не уверен, что компилятор ожидает меня от T, когда он используется в качестве фактического аргумента функции.

Я искал вокруг совсем немного и не смог взломать этот. Часть меня подозревает, что если бы я мог понять, как имя интерфейса преобразуется в тип IID: TGUID во время компиляции, при использовании конкретного имени интерфейса я мог бы добиться некоторого прогресса, но это тоже уклонилось от меня.

Любая помощь очень ценится.

Ответ 1

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

Имя интерфейса преобразуется в GUID компилятором, который ищет имя в таблице символов, получает структуру данных компилятора, представляющую интерфейс, и проверяет соответствующее поле для GUID. Но дженерики не похожи на шаблоны С++; они должны быть скомпилированы и проверены типом и, как известно, работают для любого допустимого параметра типа, а это означает ограничение параметра типа в его объявлении.

Вы можете получить GUID с помощью RTTI (сначала проверяя, что T действительно представляет интерфейс) с чем-то вроде GetTypeData(TypeInfo(T))^.Guid и таким образом передают GUID на Supports.

Ответ 2

Почему вы даже беспокоитесь?

Чтобы использовать этот TFunctions.GetInterface вам нужно:

  • интерфейс
  • ссылка на объект

Если у вас есть это, вы можете просто вызвать Supports() напрямую:

  intf := TFunctions.GetInterface<IMyInterface>(myObject);

в точности эквивалентно:

  Supports(IMyInterface, myObject, intf);

Использование дженериков здесь пустая трата времени и усилий и действительно вызывает вопрос "Зачем это делать?".

Это просто усложняет чтение (как это часто бывает с дженериками) и более громоздко использовать.

Поддержка() возвращает удобное логическое значение для указания успеха/неудачи, которое вы должны испытывать отдельно, используя свою обертку:

  intf := TFunctions.GetInterface<IMyInterface>(myObject);
  if Assigned(intf) then
    // ...

против

  if Supports(IMyInterface, myObject, intf) then
    // We can use intf

При создании оболочек вокруг функциональности обычно бывает, что результатом является улучшение читабельности или удобства использования.

imho это не работает по обоим пунктам, и вы должны просто придерживаться самой функции Поддержка().