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