Login | Register   
LinkedIn
Google+
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Code Around C#'s Using Statement to Release Unmanaged Resources : Page 2

Find out how to avoid the kind of resource consumption that impedes your application's performance or even stops it from working altogether.


advertisement
Wrapping Up
In order to release the unmanaged resources obtained during construction as soon as possible, consider employing:
  • The using statement if an exception can be thrown during the usage part.
  • Explicit try/catch blocks in the constructor if an exception can be thrown from the constructor.
  • You may prefer to allocate resources outside of the constructors, allowing them to initialize only data members, thus constructing your memory models in two stages. In this case, the using statement will work as it was intended.

New Roadblocks
Adding try/catch blocks in the constructor and releasing unmanaged resources brings an additional issue into discussion: How do you return from the failed constructor? One possible way is to rethrow the exception. Another method is to throw another exception and then catch it in the calling code. You may not like over-engineering with the additional try\catch block around the using statement that is required in these cases. So another way is to use an A constructor with parameters instead of the parameterless constructor shown above. You may set the bool Result status flag in the o object, which is then passed to the A(UserRefType o) constructor in the using statement. In this case, you would add something like if (o.Result) after returning from the constructor. If you dislike using status flags, you might also consider dropping the using statement altogether and employing just a try/catch/finally block.

Take care to carefully construct the Dispose() method with the following considerations in mind:

  • The method may need to know how the constructor or usage block was exited—at its end or after an exception was thrown somewhere in its body. It might be necessary to execute some actions—for instance “undo”—based on this knowledge.
  • The method may throw its own exceptions before it processes a thrown exception. This behavior will mask the original exception and may change an expected execution order.
  • The method may change the state of the object that could be passed to the A(UserRefType o) constructor. This new state may cause runtime errors or misbehavior when the o object is used later.



Additional Considerations
The following code presents an IL instruction that looks at the value on the stack:

IL_0009: brfalse.s IL_0011

If it is 0, or a null reference, it will break. Generally, having this instruction prevents failure to call the Dispose() method when c is null, as in the following code:

C c = C(); //C does not derive from IDisposable using(c as IDisposable) { . . . }

Because c must be of the System.IDisposable type or a type that can be implicitly converted to System.IDisposable, you need to use the as operator to allow the C# compiler to compile this code without errors.

This article doesn't discuss why you would write code like this—it just highlights the IL implementation of the finally block. Of course, if C is derived from IDisposable() and the C() constructor failed, Dispose() will not be called here either.

Those who are familiar with native C++ and the RAII (Resource Acquisition Is Initialization) pattern will remember that the following block:

{ classX x; //x is allocated on the stack //point3 ….. //point4 }

guarantees the call of the ~ClassX() destructor—even if an exception has been thrown in this block between point3 and point4. If the constructor throws an exception, the object will not be fully constructed, and it won't call the destructor. In that case, those resources allocated before the exception is thrown will be leaked.

In .NET, the garbage collector will call the Finalize() method (only if you provide the destructor) on the object to clean up its unmanaged resources. Therefore, by providing an implementation for the destructor, like ~A() {Dispose(false);}, you eliminate leaks of unmanaged resources. Of course, the time when this destructor is called is non-deterministic, and it takes longer for the Finalize() to be called.

In order to be able to step through your IL code with the DbgCLR debugger, will need to do the following:

  • Build your project and create the <YourApplicationName>.exe output file.
  • Create <YourApplicationName>.il file.
    • Run the ildasm.exe MS .NET Framework IL Disassembler.
    • Open <YourApplicationName>.exe file.
    • Dump (save As) <YourApplicationName>.il file.
  • Finish the round trip, create the <YourApplicationName>.exe (PE format) and <YourApplicationName>.pdb files.
    • Run the ilasm.exe MS .NET Framework IL Assembler and specify the <YourApplicationName>.il filename and/debug option.



Boris Eligulashvili is a Senior Engineer at Viewray, Inc. Prior to joining Viewray, he worked his way through different software development positions for Allen-Bradley, Saint-Gobain Crystals and Detectors, Philips Medical Systems, and Hitachi Medical Systems America, Inc. He has worked with the latest technologies as they have evolved throughout the years—from raw machine code to .NET. His current focus is in developing software solutions for a novel MRI-guided radiotherapy device.
Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap