Object life-cycle management with using clauses
If you have an object with a complete life
cycle in a particular extent of code, you can simplify your code with
the using statement. The
using statement is a more
compact syntax and less error-prone way to work with resources than using
try/catch/finally clauses. With using clauses:
- Cleanup always occurs without
requiring a separate
finallyclause. - You do not need to explicitly
check whether variables for initialized resources have
nullvalues. - Code for locking and synchronizing resources is simplified.
For example, suppose you want to use an output stream.
Typical code would open the stream, then use it, then close the stream
to dispose of related resources. If something goes wrong while using
the output stream, your code must close the output stream and perhaps
check whether it successfully opened before closing it. In Gosu or standard
Java, you could use a try/finally block like the following
to clean up the stream:
OutputStream os = SetupMyOutputStream() // Insert your code that creates your output stream
try {
// Do something with the output stream
}
finally {
os.close();
}
You can simplify that code by using the Gosu using statement:
using ( var os = SetupMyOutputStream() ) {
// Do something with the output stream
} // Gosu disposes of the stream after it completes or if there is an exception
The basic form of a using
clause is as follows:
using ( ASSIGNMENT_OR_LIST_OF_STATEMENTS ) {
// Do something here
}
The parentheses after the using keyword can contain either
a Gosu expression or a list of one or more Gosu statements delimited
by commas, not semicolons.
Several categories of objects
work with the using keyword:
disposable objects, closeable objects, and reentrant objects. If you try to use an object that does
not satisfy the requirements of one of these categories, Gosu displays
a compiler error.
If Gosu detects that an object is in more
than one category, at run time, Gosu considers the object to be in only
one category. Gosu selects the category by the following precedence:
disposable, closeable, reentrant. For example, if an object has a dispose and close method, Gosu only calls
the dispose method.
Use the return
statement to return values from using
clauses.
If Gosu successfully evaluates and initializes all variables, each variable is examined in order. For each object, if the object has an enter action, that action is taken:
- If the object implements the
Lockinterface, Gosu callsLock.lock() - If the object implements
the
IReentrantinterface, Gosu callsIRenntrant.enter() - If the object has a a
method named
lockand it takes no parameters, Gosu calls the lock method. - If the object was cast
to the
IMonitorLockinterface, Gosu synchronizes on the variable
If the enter action completes without an exception,
Gosu guarantees that the corresponding exit action for that object will
run. If no enter action applies to the object, Gosu guarantees that the
exit action for the object will run. Exit actions are called in the reverse
order of declaration in the using
clause. Exit actions are as follows:
- If the object implements the
IDisposableinterface, Gosu callsIDisposable.dispose() - If the object has a method
named
disposeand it takes no parameters, Gosu calls the method. - If the object implements
the
Closeableinterface, Gosu callsCloseable.close() - If the object has a method
named
closeand it takes no parameters, Gosu calls the method. - If the object implements
the
IReentrantinterface, Gosu callsIRenntrant.exit() - If the object implements
the
Lockinterface, Gosu callsLock.unlock() - If the object has a method named
unlockthat takes no parameters, Gosu calls the unlock method. - If the object was cast
to the
IMonitorLockinterface, Gosu unsynchronizes on the variable
If an exception occurs during the execution of an exit action of an object, the exit actions defined before that exit action run, and then the exception throws. If an exception occurs in more than one exit action, the outermost exception, which occurs later in the code, takes precedence and is thrown to the surrounding code.
