most of the time they are not needed in pascal eg
function Open(const aFilename:TFilename):TFileStream;
begin
result := tfilestream.create(afilename,fmopenreadwriteorfmshareexclusive);
end;
procedure domytheng;
begin
with open("MyDataFile.Dat") do
try
read(Data, size);
finally
free
end;
end;
but I agree it seems a bit more care free with out all that try/finally stuff ofcourse it shifts the responsibility for a proper destruction to the component writer instead it does not go away and I do not think that any component writer would like to take on more than it is absolutely necessary (my included). Look what happened with the build in for in support do you know many 3rd party controls that have build support?
Imagine a complex system where you have to create or use some context before calling some of API functions.
You'll create similar try/finally blocks in different parts of your code. That'll take a lot of line, lower readability and maintainability.
What if you have 2 or more logically unrelated contexts that you have to create or use in one function? All they would go into your one try/finally block.
Using TheCustomer, SomeOtherObject to name1, OtherHelperContext to name2 do
begin
// here needed environment(context) is ensured by these "context managers" above
name1.someMethod;
name2.interestingMethod;
end;
All objects in a
using statement must implement intrinsic interface:
IContextManager = Interface
// the actual type determined at compilation in class implementation
// it can be implemented as a procedure in which case "to name1" suffix will not be allowed
intrinsic __enter__: DeferredType; // RETURNED VALUE ASSIGNED to "name1", "name2" above
procedure __exit__;
procedure __rescue__(ex: Exception); // called when "using" statement raises an exception
end;
So, that construction somehow partly replaces RAII of C++, gives some "design by contract" abilities.
Also it can be used to program transactions with rollbacks.
Or using draw procedures in some graphic contexts, or action procedures in a game.
To repeat:
1) Compiler inserts calls to __enter__ implementation when compiling "
using" statement, before begin..end, so to speak.
__enter__ implementations can return type that are assigned to name in "
using" statement clauses.
2) At the end compiler inserts calls to __exit__ procedures.
If exception occurs, it should call __resque__ procedures.