Использование функции Supports () с универсальным типом интерфейса - PullRequest
10 голосов
/ 11 декабря 2010

Я только что попробовал свое первое использование обобщений в 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 '(' ожидается, но ',' найдено [Ошибка DCC] GenericExample.pas (37): оператор E2014 ожидается, но выражение типа 'T' найдено

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

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

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

Ответы [ 2 ]

10 голосов
/ 11 декабря 2010

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

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

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

1 голос
/ 12 декабря 2010

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

Для использования этого TFunctions.GetInterface вам необходимо:

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

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

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

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

  Supports(IMyInterface, myObject, intf);

Использование здесь обобщенийэто пустая трата времени и усилий, и на самом деле напрашивается вопрос «зачем это делать?».

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

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

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

против:

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...