День добрый.
Думаю, все дельфийцы тысячи раз писали подобный код:
Скажем так: такая писанина надоедает. Хочется, чтоб объекты сами уничтожались при выходе из функции/процедуры.
Что ж, это достаточно легко реализовать. В результате мы получим примерно такой код:
Ниже я опишу как реализовать такое поведение.
Сразу укажу, что все код ниже будет работать во всех версиях дельфи, в которых есть интерфейсы (по моему начиная с третьей).
Как известно для интерфейсов Delphi ведет автоматический счет ссылок:
Теперь напишем наследника TInterfacedObject, который будет уничтожать в своем деструкторе переданный ему объект. Ну и плюс наш объект будет реализовывать маленький дополнительный интерфейс.
Теперь можно работать с объектом так:
Мы избавились от необходимости явно уничтожать MyObj, но необходимость для доступа к объекту писать TMyObj(Destroyer.ObjectAsPtr) мягко говоря удручает. Ну что ж, опишем доп функцию:
и уберем из нашего кода в общем то ненужную нам переменную Destroyer:
Вызов CreateObjectDestroyer неявно создаем переменную (то что в прошлом варианте мы описали как Destroyer).
Ну собственно задача решена, но хотелось бы упоминать о некоторых неочевидных моментах и откровенных недостатках такого подхода:
Итак, первое — типизация. Точнее её отсутствие. свойство ObjectAsPtr специально объявлено как Pointer чтобы его можно было присвоить переменной любого класса:
но в таком случае компилятор не следит за типами и мы можем накосячить:
скорее всего в таком случае на ждем Access violation
Можно добавить в интерфейс IObjectDestroyer свойство AObject: TObject и в явном виде приводить типы
тут ошибка будет более явной, но все равно это проверка в ран-тайм. В компил тайм для дельфей младше 2009 версии эта проблема не решается, в старших версиях появились дженерики, там можно типизацией не жертвовать.
Вторая проблема: неявный момент уничтожения объекта.
В теории компилятор может вызвать Release для непосредственно после вызова CreateObjectDestroyer
Obj := CreateObjectDestroyer(TMyObj.Create).ObjectAsPtr;
// Ссылка Obj невалидна так как интерфейс зарелизин и объект MyObj уничтожен.
Однако Barry Kelly говорит, что такое поведении не будет меняться в будущем, можно почитать обсуждение здесь
Опять же как и вопрос с типизацией в 2009/2010 дельфе можно частично решить и данный недостаток.
Ну в общем то все, если Вам понравится этот пост, я опишу как более прозрачно реализовать аналогичный механизм на старших версиях дельфи.
ЗЫ Посоветуйте аналог source code highlighter, который бы поддерживал дельфийский синтаксис?
Думаю, все дельфийцы тысячи раз писали подобный код:
var
MyObj: TMyObj;
begin
MyObj := TMyObj.Create;
try
MyObj.DoWork; // работаем с MyObj
finally
MyObj.Free;
end;
end;
Скажем так: такая писанина надоедает. Хочется, чтоб объекты сами уничтожались при выходе из функции/процедуры.
Что ж, это достаточно легко реализовать. В результате мы получим примерно такой код:
var
MyObj: TMyObj;
begin
MyOb := CreateObjectDestroyer(TMyOb.Create).ObjectAsPtr;
MyObj.DoWork; // работаем с MyObj
end; // тут объект MyObj уничтожится
Ниже я опишу как реализовать такое поведение.
Сразу укажу, что все код ниже будет работать во всех версиях дельфи, в которых есть интерфейсы (по моему начиная с третьей).
Как известно для интерфейсов Delphi ведет автоматический счет ссылок:
var
Intf: IUnknown;
begin
Intf := TInterfacedObject.Create; // тут неявно вызывается IUnknown.AddRef
// что то делаем
end; // тут неявно вызывается IUnknown.Release и объект разрушается
Теперь напишем наследника TInterfacedObject, который будет уничтожать в своем деструкторе переданный ему объект. Ну и плюс наш объект будет реализовывать маленький дополнительный интерфейс.
type
IObjectDestroyer = interface(IInterface)
['{4DE81104-08B2-4821-960E-8935AC9B8F5E}']
function GetObjectAsPtr: Pointer;
property ObjectAsPtr: Pointer read GetObjectAsPtr; // это ссылка на тот объект, который мы хотим уничтожить
end;
type
TObjectDestroyer = class(TInterfacedObject, IObjectDestroyer)
strict private
FObject: TObject;
protected
function GetObjectAsPtr: Pointer;
public
constructor Create(AObject: TObject);
destructor Destroy; override;
end;
constructor TObjectDestroyer.Create(AObject: TObject);
begin
inherited Create();
FObject:=AObject;
end;
destructor TObjectDestroyer.Destroy;
begin
FObject.Free;
inherited Destroy;
end;
function TObjectDestroyer.GetObjectAsPtr: Pointer;
begin
Result := FObject;
end;
Теперь можно работать с объектом так:
var
Destroyer: IObjectDestroyer ;
begin
Destroyer := TObjectDestroyer.Create(TMyObj.Create);
TMyObj(Destroyer.ObjectAsPtr).DoWork();
end;
Мы избавились от необходимости явно уничтожать MyObj, но необходимость для доступа к объекту писать TMyObj(Destroyer.ObjectAsPtr) мягко говоря удручает. Ну что ж, опишем доп функцию:
function CreateObjectDestroyer(AObject: TObject): IObjectDestroyer;
begin
Result:=TObjectDestroyer.Create(AObject);
end;
и уберем из нашего кода в общем то ненужную нам переменную Destroyer:
var
MyObj: TMyObj;
begin
MyOb := CreateObjectDestroyer(TMyOb.Create).ObjectAsPtr;
MyObj.DoWork;
end;
Вызов CreateObjectDestroyer неявно создаем переменную (то что в прошлом варианте мы описали как Destroyer).
Ну собственно задача решена, но хотелось бы упоминать о некоторых неочевидных моментах и откровенных недостатках такого подхода:
Итак, первое — типизация. Точнее её отсутствие. свойство ObjectAsPtr специально объявлено как Pointer чтобы его можно было присвоить переменной любого класса:
var
Obj1: TMyObj1;
Obj2: TMyObj2;
begin
Obj1 := CreateObjectDestroyer(TMyObj1.Create).ObjectAsPtr;
Obj2 := CreateObjectDestroyer(TMyObj2.Create).ObjectAsPtr;
end;
но в таком случае компилятор не следит за типами и мы можем накосячить:
Obj1 := CreateObjectDestroyer(TMyObj2.Create).ObjectAsPtr;
скорее всего в таком случае на ждем Access violation
Можно добавить в интерфейс IObjectDestroyer свойство AObject: TObject и в явном виде приводить типы
Obj1 := CreateObjectDestroyer(TMyObj2.Create).AObject as TMyObj1;
тут ошибка будет более явной, но все равно это проверка в ран-тайм. В компил тайм для дельфей младше 2009 версии эта проблема не решается, в старших версиях появились дженерики, там можно типизацией не жертвовать.
Вторая проблема: неявный момент уничтожения объекта.
В теории компилятор может вызвать Release для непосредственно после вызова CreateObjectDestroyer
Obj := CreateObjectDestroyer(TMyObj.Create).ObjectAsPtr;
// Ссылка Obj невалидна так как интерфейс зарелизин и объект MyObj уничтожен.
Однако Barry Kelly говорит, что такое поведении не будет меняться в будущем, можно почитать обсуждение здесь
Опять же как и вопрос с типизацией в 2009/2010 дельфе можно частично решить и данный недостаток.
Ну в общем то все, если Вам понравится этот пост, я опишу как более прозрачно реализовать аналогичный механизм на старших версиях дельфи.
ЗЫ Посоветуйте аналог source code highlighter, который бы поддерживал дельфийский синтаксис?