匿名方法本质上是带有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”和“procedure, untyped pointer or untyped parameter”
问题:这个有什么用途?
有很多对象无法仅通过接口引用来保持其存在。因此,它们被封装在闭包中,并随之销毁,“智能指针”(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
。然后,为了创建一个独立的引用,新对象和字典的闭包都会进入一个新的闭包中:
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
类型的值分配给接口类型,这意味着没有统一、安全和未来可靠的方式来引用所有闭包类型以保持它们的生命周期。
IInterface
引用而不是仅持有方法引用的好处。但是,了解到您可以编写class(TInterfacedObject, TProc)
确实让我感到很有趣! - David Heffernan