Automatic Resource Management, V.2
paul.martin at gmail.com
paul.martin at gmail.com
Mon Apr 20 15:36:21 PDT 2009
Hi,
On Apr 20, 2009 10:32pm, Neal Gafter <neal at gafter.com> wrote:
> On Mon, Apr 20, 2009 at 2:07 PM, Joshua Bloch jjb at google.com> wrote:
...
>> In C# (as you know) IEnumerator extends IDisposeable.
> I don't know that because it is not true. IEnumerator does not extend
> IDisposable
> (http://msdn.microsoft.com/en-us/library/system.collections.ienumerator.aspx),
> though IEnumerator does
> (http://msdn.microsoft.com/en-us/library/78dfe2yb.aspx). So as you can
> see, that's not necessary to get the desired behavior.
MSDN's foreach documentation actually describes C#'s expansion, which does
seem to use reflection-style type casting to determine whether an
enumerator implements IDisposable or not.
From http://msdn.microsoft.com/en-us/library/aa664754.aspx
(start quote)
If the collection expression is of a type that implements the collection
pattern (as defined above), the expansion of the foreach statement is:
Copy Code
E enumerator = (collection).GetEnumerator();
try {
while (enumerator.MoveNext()) {
ElementType element = (ElementType)enumerator.Current;
statement;
}
}
finally {
IDisposable disposable = enumerator as System.IDisposable;
if (disposable != null) disposable.Dispose();
}
Significant optimizations of the above are often easily available. If the
type E implements System.IDisposable, then the expression (enumerator as
System.IDisposable) will always be non-null and the implementation can
safely substitute a simple conversion for a possibly more expensive type
test. Conversely, if the type E is sealed and does not implement
System.IDisposable, then the expression (enumerator as System.IDisposable)
will always evaluate to null. In this case, the implementation can safely
optimize away the entire finally clause.
(end quote)
I expect that a similar mechanism would be possible in Java, though there
would always be a trade-off on performance (in particular, I don't see how
the runtime check can be avoided if an interface/non-final class is used).
Another perspective may be to consider that with try (resource) { } (and
C#'s using), the programmer is explicitly stating that automatic resource
management is required; the purpose of this is mainly to simplify the error
handling code - it is not to prevent the programmer from forgetting to take
any action to close the resource. However, automatically closing the
Iterator in a for-each loop seems different to that - it is now the
compiler that is making the decision that a resource (may) need to be
closed.
This perspective could suggest that a way of explicitly indicating to the
for-each loop that the Iterator is in fact AutoDisposable might be useful
(even if its interface would ordinarily suggest otherwise at compile-time).
Candidate solutions could include:
- Casting the Iterable<T> to (a subtype of) AutoDisposableIterable<T> where
necessary (which then returns AutoDisposableIterator<T>). This would
probably mean that close() would be defined as throwing Exception however,
unless a cast to a subtype was used.
- try for (T : myIterable) { } (though that specific syntax is probably
ambiguous)
- for try (T : myIterable) { } (would be ok?)
Analysis tools such as FindBugs could potentially be extended to catch many
(though not all) errors where AutoDisposable Iterators/Iterables are being
used incorrectly (whatever the solution).
Another alternative might be to have the try use a factory method that
returns decorating Iterable, which when closed by the ARM block also closes
the associated Iterator. However that is probably nastier than using a
normal for ( ; ;) loop instead.
Regards,
Paul
More information about the coin-dev
mailing list