C# lets me do the following (example from MSDN):
using (Font font3 = new Font("Arial", 10.0f), font4 = new Font("Arial", 10.0f)) { // Use font3 and font4. }
What happens if font4 = new Font
throws? From what I understand font3 will leak resources and won't be disposed of.
using(... , ...)
should be avoided altogether in favor of nested using?In computer science, a resource leak is a particular type of resource consumption by a computer program where the program does not release resources it has acquired. This condition is normally the result of a bug in a program.
The Scanner should be closed. It is a good practice to close Readers, Streams...and this kind of objects to free up resources and aovid memory leaks; and doing so in a finally block to make sure that they are closed up even if an exception occurs while handling those objects.
No.
The compiler will generate a separate finally
block for each variable.
The spec (§8.13) says:
When a resource-acquisition takes the form of a local-variable-declaration, it is possible to acquire multiple resources of a given type. A
using
statement of the formusing (ResourceType r1 = e1, r2 = e2, ..., rN = eN) statement
is precisely equivalent to a sequence of nested using statements:
using (ResourceType r1 = e1) using (ResourceType r2 = e2) ... using (ResourceType rN = eN) statement
UPDATE: I used this question as the basis for an article which can be found here; see it for additional discussion of this issue. Thanks for the good question!
Though Schabse's answer is of course correct and answers the question that was asked, there is an important variant on your question you did not ask:
What happens if
font4 = new Font()
throws after the unmanaged resource was allocated by the constructor but before the ctor returns and fills infont4
with the reference?
Let me make that a little bit more clear. Suppose we have:
public sealed class Foo : IDisposable { private int handle = 0; private bool disposed = false; public Foo() { Blah1(); int x = AllocateResource(); Blah2(); this.handle = x; Blah3(); } ~Foo() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!this.disposed) { if (this.handle != 0) DeallocateResource(this.handle); this.handle = 0; this.disposed = true; } } }
Now we have
using(Foo foo = new Foo()) Whatever(foo);
This is the same as
{ Foo foo = new Foo(); try { Whatever(foo); } finally { IDisposable d = foo as IDisposable; if (d != null) d.Dispose(); } }
OK. Suppose Whatever
throws. Then the finally
block runs and the resource is deallocated. No problem.
Suppose Blah1()
throws. Then the throw happens before the resource is allocated. The object has been allocated but the ctor never returns, so foo
is never filled in. We never entered the try
so we never enter the finally
either. The object reference has been orphaned. Eventually the GC will discover that and put it on the finalizer queue. handle
is still zero, so the finalizer does nothing. Notice that the finalizer is required to be robust in the face of an object that is being finalized whose constructor never completed. You are required to write finalizers that are this strong. This is yet another reason why you should leave writing finalizers to experts and not try to do it yourself.
Suppose Blah3()
throws. The throw happens after the resource is allocated. But again, foo
is never filled in, we never enter the finally
, and the object is cleaned up by the finalizer thread. This time the handle is non-zero, and the finalizer cleans it up. Again, the finalizer is running on an object whose constructor never succeeded, but the finalizer runs anyways. Obviously it must because this time, it had work to do.
Now suppose Blah2()
throws. The throw happens after the resource is allocated but before handle
is filled in! Again, the finalizer will run but now handle
is still zero and we leak the handle!
You need to write extremely clever code in order to prevent this leak from happening. Now, in the case of your Font
resource, who the heck cares? We leak a font handle, big deal. But if you absolutely positively require that every unmanaged resource be cleaned up no matter what the timing of exceptions is then you have a very difficult problem on your hands.
The CLR has to solve this problem with locks. Since C# 4, locks that use the lock
statement have been implemented like this:
bool lockEntered = false; object lockObject = whatever; try { Monitor.Enter(lockObject, ref lockEntered); lock body here } finally { if (lockEntered) Monitor.Exit(lockObject); }
Enter
has been very carefully written so that no matter what exceptions are thrown, lockEntered
is set to true if and only if the lock was actually taken. If you have similar requirements then what you need to to is actually write:
public Foo() { Blah1(); AllocateResource(ref handle); Blah2(); Blah3(); }
and write AllocateResource
cleverly like Monitor.Enter
so that no matter what happens inside AllocateResource
, the handle
is filled in if and only if it needs to be deallocated.
Describing the techniques for doing so is beyond the scope of this answer. Consult an expert if you have this requirement.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With