Подтвердить что ты не робот

Назначить анонимный метод интерфейсной переменной или параметру?

Анонимные методы по существу interface с помощью метода Invoke:

type
  TProc = reference to procedure;

  IProc = interface
    procedure Invoke;
  end;

Теперь, есть ли возможность назначить их фактической интерфейсной переменной или передать их как параметр интерфейса?

procedure TakeInterface(const Value: IInterface);
begin
end;

var
  P: TProc;
  I: IInterface;
begin
  I := P; // E2010
  TakeInterface(P); // E2010
end;

[Ошибка DCC32] E2010 Несовместимые типы: "IInterface" и "процедура, нетипизированный указатель или нетипизированный параметр"

Вопрос: Каким будет прецедент для этого?

Есть много объектов, которые не могут просто поддерживаться ссылкой на интерфейс. Поэтому они завертываются в закрытие и уничтожаются вместе с ним "Умные указатели" :

type
  I<T> = reference to function : T;

  TInterfaced<T: class> = class (TInterfacedObject, I<T>)
  strict private
    FValue: T;
    function Invoke: T; // Result := FValue;
  public
    constructor Create(const Value: T); // FValue := Value;
    destructor Destroy; override; // FValue.Free;
  end;

  IInterfacedDictionary<TKey, TValue> = interface (I<TDictionary<TKey, TValue>>) end;

  TKey = String;
  TValue = String;

var
  Dictionary: IInterfacedDictionary<TKey, TValue>;
begin
  Dictionary := TInterfaced<TDictionary<TKey, TValue>>
    .Create(TDictionary<TKey, TValue>.Create);
  Dictionary.Add('Monday', 'Montag');
end; // FRefCount = 0, closure with object is destroyed

Теперь иногда необходимо не только сохранить один живой объект, но и контекст с ним. Представьте, что у вас есть TDictionary<TKey, TValue> и вытащите из него счетчик: TEnumerator<TKey>, TEnumerator<TValue> или TEnumerator<TPair<TKey, TValue>>. Или словарь содержит и имеет TObject s. Затем оба объекта, новый объект и закрытие словаря войдут в новое замыкание, чтобы создать одну единственную автономную ссылку:

type
  TInterfaced<IContext: IInterface; T: class> = class (TInterfacedObject, I<T>)
  strict private
    FContext: IContext;
    FValue: T;
    FFreeObject: Boolean;
    function Invoke: T; // Result := FValue;
  public
    constructor Create(const Context: IContext; const Value: T; const FreeObject: Boolean = True); // FValue = Value; FFreeObject := FreeObject;
    destructor Destroy; override; // if FFreeObject then FValue.Free;
  end;

  IInterfacedEnumerator<T> = interface (I<TEnumrator<T>>) end;

  TValue = TObject; // 

var
  Dictionary: IInterfacedDictionary<TKey, TValue>;
  Enumerator: IInterfacedEnumerator<TKey>;
  Obj: I<TObject>;
begin
  Dictionary := TInterfaced<TDictionary<TKey, TValue>>
    .Create(TObjectDictionary<TKey, TValue>.Create([doOwnsValues]));
  Dictionary.Add('Monday', TObject.Create);

  Enumerator := TInterfaced<
    IInterfacedDictionary<TKey, TValue>,
    TEnumerator<TKey>
  >.Create(Dictionary, Dictionary.Keys.GetEnumerator);

  Obj := TInterfaced<
    IInterfacedDictionary<TKey, TValue>,
    TObject
  >.Create(Dictionary, Dictionary['Monday'], False);

  Dictionary := nil; // closure with object still held alive by Enumerator and Obj.
end;

Теперь идея состоит в том, чтобы расплавить TInterfaced<T> и TInterfaced<IContext, T>, что сделает параметр типа для устаревшего контекста (интерфейса достаточно) и приведет к этим константам:

constructor TInterfaced<T: class>.Create(const Value: T; const FreeObject: Boolean = True); overload;
constructor TInterfaced<T: class>.Create(const Context: IInterface; const Value: T; const FreeObject: Boolean = True); overload;

Быть (чистым) закрытием может быть не основным, о котором можно было бы подумать при работе с анонимными методами. Однако их типы могут быть заданы как интерфейс класса, объекты которого могут выполнять очистку при уничтожении замыкания, а TFunc<T> обеспечивает свободный доступ к его контенту. Хотя они не имеют общего предка, и, похоже, значения типов reference to не могут быть присвоены типам интерфейсов, а это означает, что нет единого, безопасного и будущего безопасного способа ссылаться на все типы замыканий, чтобы сохранить их в живых.

4b9b3361

Ответ 1

Это очень просто. Я покажу вам два пути.

var
  P: TProc;
  I: IInterface;
begin
  I := IInterface(Pointer(@P)^);
  TakeInterface(I);
end;

Другой способ - объявить PInterface

type
  PInterface = ^IInterface;
var
  P: TProc;
  I: IInterface;
begin
  I := PInterface(@P)^;
  TakeInterface(I);
end;

Ответ 2

Насколько мне известно, вы не можете делать то, что вам нужно, с помощью кастинга.

Вы можете, я полагаю, использовать Move для выполнения задания:

{$APPTYPE CONSOLE}
type
  TProc = reference to procedure(const s: string);
  IProc = interface
    procedure Invoke(const s: string);
  end;

procedure Proc(const s: string);
begin
  Writeln(s);
end;

var
  P: TProc;
  I: IProc;

begin
  P := Proc;
  Move(P, I, SizeOf(I));
  I._AddRef;//explicitly take a reference since the compiler cannot do so
  I.Invoke('Foo');
end.

Я честно понятия не имею, насколько это здорово. Будет ли он работать с несколькими версиями Delphi? Целесообразно ли полагаться на неясные недокументированные детали реализации? Только вы можете определить, превышают ли ваши выигрыши негативы, связанные с деталями реализации.