{$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.