delphi 是否将匿名方法赋给接口变量或参数?

piah890a  于 2022-11-04  发布在  其他
关注(0)|答案(3)|浏览(170)

匿名方法本质上是带有Invoke方法的interface

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'和'程序,不具型别指标或不具型别参数'

问题:此功能的使用情形是什么?

有很多对象不能简单地通过接口引用来保持活动状态,因此它们被 Package 在闭包中并被销毁,"Smart Pointers"

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类型的值似乎不能被分配给接口类型,这意味着,没有统一的、安全的和未来的方法来引用所有类型的闭包以使它们保持活动。

rkue9o1l

rkue9o1l1#

这很简单,我给你两条路.

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;
tpxzln5u

tpxzln5u2#

据我所知,你不能做你需要的铸造。
我想,你可以用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 版本上工作吗?依赖晦涩的未记录的实现细节是明智的吗?只有你能确定你所获得的收益是否超过依赖实现细节所带来的负面影响。

b4wnujal

b4wnujal3#

最简单的转换方法如下:

IProc((@P)^)

相关问题