I do not quite understand the use / disposable objects work

I asked a question about returning a Disposable ( IDisposable ) object from a function , but I thought I would confuse the discussion if I raised this question there.

I have created some code examples:

 class UsingTest { public class Disposable : IDisposable { public void Dispose() { var i = 0; i++; } } public static Disposable GetDisposable(bool error) { var obj = new Disposable(); if (error) throw new Exception("Error!"); return obj; } } 

I encoded it so intentionally because then I call:

 using (var tmp = UsingTest.GetDisposable(true)) { } 

Using the debugger, I notice that the Dispose method is never executed, although we have already created an instance of the Disposable object. If I understood the purpose of Dispose correctly, if this class actually opened descriptors and the like, we would not close them as soon as we finished with them.

I ask this question because this behavior is consistent with what I would expect, but in the answers to the corresponding question, people seemed to indicate that using would take care of everything.

If using still takes care of all this, can someone explain what I am missing? But if this code can really cause a resource leak, how would you suggest the GetDisposable code ( GetDisposable that I have to instantiate the IDisposable and run the code that can throw an exception before the return statement)?

+6
c # idisposable using-statement
source share
6 answers

Depending on what kind of semantics you want to use for GetDisposable , probably I would implement it:

 public static Disposable GetDisposable(bool error) { var obj = new Disposable(); try { if (error) throw new Exception("Error!"); return obj; } catch (Exception) { obj.Dispose(); throw; } } 
+4
source share

The reason it was never called is related to how you distribute it. The variable "tmp" is never highlighted at all, because the GetDisposable(bool) function never returns because you selected an exception.

If you say the following instead:

 using (var tmp = new Disposable()) { throw new ArgumentException("Blah"); } 

then you will see that IDisposable::Dispose() indeed called.

The most important thing to understand is that the using block should get a valid reference to the IDisposable object. If an exception occurs so that the variable declared in the using block is not assigned, you are out of luck because the using block will not know the IDisposable object.

Regarding returning an IDisposable object from a function, you should use the standard catch block inside the function to call Dispose() in case of failure, but obviously you should not use using because it will destroy the object before you are ready to do it yourself.

+11
source share

This is because the tmp variable is never assigned. This is what you need to be careful with disposable objects. The best definition for a GewtDisposable would be:

 public static Disposable GetDisposable(bool error) { var obj = new Disposable(); try { if (error) throw new Exception("Error!"); return obj; } catch { obj.Dispose(); throw; } } 

Because it provides obj placement.

+3
source share

The IDisposable interface simply ensures that the class that implements it has a Dispose method. It does nothing to call this method. When using a block, Dispose will be called on the object.

+1
source share

You create IDisposable in GetDisposable , but since you exit the function by throwing an exception, it never returns, and therefore, tmp never assigned. The using statement is short for

 var tmp = UsingTest.GetDisposable(true); try { } finally { if(tmp != null) tmp.Dispose(); } 

and you will never get to the try block. The solution in your example is to check the error flag before creating a one-time obj object:

 public static Disposable GetDisposable(bool error) { if (error) throw new Exception("Error!"); return new Disposable(); } 
+1
source share

Related question Handling iDisposable in a failed initializer or constructor , and I think the answer is that if you want to avoid leakage of disposable objects from the failed constructor, you will need to pull a copy of the object from the constructor (for example, put it in a container with by passing it or assigning it a mediation variable) and wrap the constructor call in a catch block. Ikki, but I don’t know how to do it better. VB.net can actually manage a little better than C # because of how its initializers work.

0
source share

All Articles