{$mode objfpc} {$h+}
uses
SysUtils;
type
IAutoClosingFile = interface
procedure Write(const s: string);
end;
// Представим, что это файл, который может быть открыт только эксклюзивно —
// не более 1 раза одновременно.
TAutoClosingFile = class(TInterfacedObject, IAutoClosingFile)
constructor Create;
destructor Destroy; override;
procedure Write(const s: string);
private
opened: boolean;
class var
GloballyOpened: boolean;
end;
constructor TAutoClosingFile.Create;
begin
inherited Create;
if GloballyOpened then
begin
writeln('Нельзя открыть файл: он уже открыт. Бросается <Исключение>.');
raise Exception.Create('<Исключение>');
end;
writeln('Файл открыт.');
GloballyOpened := true;
opened := true;
end;
destructor TAutoClosingFile.Destroy;
begin
if opened then
begin
writeln('Файл закрыт.');
opened := false;
GloballyOpened := false;
end;
inherited Destroy;
end;
procedure TAutoClosingFile.Write(const s: string);
begin
writeln('В файл записано: "', s, '".');
end;
procedure Test1_BAD;
var
f: IAutoClosingFile;
begin
writeln('Тест 1. Произойдёт ошибка: новый файл откроется прежде, чем закроется старый.');
f := TAutoClosingFile.Create;
f.Write('test');
f := TAutoClosingFile.Create;
f.Write('test2');
end;
procedure Test2_WORKAROUND;
var
f: IAutoClosingFile;
begin
writeln('Тест 2. Обход ошибки путём форсирование финализации интерфейсной переменной через := nil (можно также Finalize).');
f := TAutoClosingFile.Create;
f.Write('test');
f := nil;
f := TAutoClosingFile.Create;
f.Write('test2');
end;
procedure Test3_WORKAROUND_2;
procedure Write1;
var
f: IAutoClosingFile;
begin
f := TAutoClosingFile.Create;
f.Write('test');
end;
procedure Write2;
var
f: IAutoClosingFile;
begin
f := TAutoClosingFile.Create;
f.Write('test2');
end;
begin
writeln('Тест 3. Обход ошибки разбиением на 2 процедуры.');
Write1;
Write2;
end;
{procedure Test4_GOOD;
begin
writeln('Тест 4. Как это могло бы быть.');
begin
var f: IAutoClosingFile = TAutoClosingFile.Create;
f.Write('test');
end;
begin
var f: IAutoClosingFile = TAutoClosingFile.Create;
f.Write('test2');
end;
end;}
procedure Run(test: TProcedure);
begin
try
test();
except
on e: Exception do writeln(e.Message + ' обработано.');
end;
writeln('---');
writeln;
end;
begin
Run(@Test1_BAD);
Run(@Test2_WORKAROUND);
Run(@Test3_WORKAROUND_2);
writeln('Оба способа обойти ошибку были магическими или неудобными.' + LineEnding +
'С inline-переменными можно было создать оба файла внутри отдельных скоупов begin-end, и они финализировались бы в правильные моменты.');
end.