Анонимните методи по същество са interface
s с 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 Несъвместими типове: „II интерфейс“ и „процедура, нетипизиран указател или нетипизиран параметър“
Въпрос: Какъв би бил случаят на използване за това?
Има много обекти, които не могат просто да бъдат поддържани живи с препратка към интерфейс. Следователно те са обвити в затваряне и се унищожават с него, "Интелигентни указатели":
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
типовете не могат да бъдат присвоени на типове интерфейси, което означава, че няма унифициран, безопасен и устойчив на бъдещето начин да се отнасят към всички типове затваряния, за да ги поддържат живи.
IInterface
за затварянето вместо простото задържане на референцията на метода. Въпреки това беше интересно да науча, че можете да напишетеclass(TInterfacedObject, TProc)
, което определено е новина за мен! - person David Heffernan   schedule 09.07.2013