Как назначить ранее существующую функцию для TComparison<T>?

program Project55;    
{$APPTYPE CONSOLE}
uses
  System.Generics.Defaults;

type
  TestRec<T> = record
    Compare: TComparison<T>;
    CompareI: IComparer<T>;
  end;

var
  TRI: TestRec<Integer>;

begin
  TRI.CompareI:= TComparer<Integer>.Default;
  TRI.Compare:= TRI.CompareI.Compare;  //E2035 Not enough actual parameters
  TRI.Compare:= @TRI.CompareI.Compare; //E2035 Not enough actual parameters
end.

Я знаю, что могу назначить тело функции как анонимную функцию, но почему я не могу назначить существующую функцию?

Конечно, следующие работы, но это просто глупо:

  TRI.Compare:= function(const L,R: integer): Integer
  begin
    Result:= TRI.CompareI.Compare(L,R);
  end;

PS. Я использую Delphi XE7, но сомневаюсь, что версия имеет значение.

3 ответа

Решение

Знаю это IComparer<T> это интерфейс только с одним методом, который имеет ту же сигнатуру, что и TComparison<T> и что анонимные методы - это просто интерфейсы с одним методом, вы можете сделать следующее.

IComparer<Integer>(TRI.Compare) := TRI.CompareI;

Я использую этот трюк в Spring4D, чтобы избежать создания объекта-обертки вокруг TComparison<T> быть принятым как IComparer<T> потому что они двоично совместимы.

Ваши попытки выполнить это назначение не удаются, потому что нельзя присвоить интерфейсный метод ссылочной переменной метода. Язык просто не позволяет этого. Типы не совместимы по назначению. Допустимыми источниками присваивания являются анонимные методы, методы классов (экземпляр или класс) и процедуры области действия модуля.

Уловки, которые можно увидеть в других ответах, зависят от глубокого знания деталей реализации. Что означает, что они могут быть изменены. Но с точки зрения языка, то, что вы пытаетесь, не допускается.

Анонимные методы не являются точно указателями методов. Они реализованы в виде интерфейса с единым методом "Invoke".

Можно извлечь указатель метода из анонимного метода, но, насколько мне известно, он опирается на текущие детали реализации анонимного метода и может быть подвержен изменениям в будущей версии delphi. Другими словами, я бы советовал против этого. Это было взято дословно из поста Барри Келли здесь. (Который охватывает тему более тщательно, чем я здесь)

procedure MethRefToMethPtr(const MethRef; var MethPtr);
type
  TVtable = array[0..3] of Pointer;
  PVtable = ^TVtable;
  PPVtable = ^PVtable;
begin
  // 3 is offset of Invoke, after QI, AddRef, Release
  TMethod(MethPtr).Code := PPVtable(MethRef)^^[3];
  TMethod(MethPtr).Data := Pointer(MethRef);
end;

Исходя из вашего примера, я бы предложил это в качестве альтернативы

type
  TestRec<T> = record
    CompareI: IComparer<T>;
    function Compare(const L, R : T) : Integer;
  end;
[...]
function TestRec<T>.Compare(const L, R : T) : Integer;
begin
  Result := CompareI.Compare(L,R);
end;

Но тогда это может / не может применяться к вашей текущей ситуации.

Другие вопросы по тегам