none
Exception handling, "new" keyword and resource freeing RRS feed

  • Question

  • Hi all,

    I am quite new to .net development (but know other paradigms and languages quite well). I have a question regarding the relationship between exception handling, resource freeing and the "new" keyword. I think I can explain my problem best by using a simple example.

    Suppose we want to open a filestream and a binary reader on the filestream. The binary reader depens on the stream, of course. Never doing evil things, we want to make sure that resources are freed if something goes wrong. The first idea is like that (I am writing pseudocode):

    try
      obj_Stream = new filestream(...)
    
      try
        obj_BinReader = new binaryreader(filestream, ...)
        'Some code which does the work
        obj_BinReader.close
      catch BinReaderEx as SomeSortOfBinaryReaderException
        'Code for handling binaryreader exception
      finally
        'Probably nothing to do here
      end try
    
    obj_Stream.close catch StreamEx as SomeSortOfFileStreamException 'Code for handling filestream exception finally 'nothing to do here
    end try

    This code looks good at the first sight. The objects are closed only if they have successfully been opened (and this is what we want). But there are two serious problems:

    1) obj_BinReader is only closed if no exception is raised after opening it; if there was an exception execution would be transferred to the catch clause of the inner try, and obj_BinReader would be left open (resource leak). The same applies to obj_Stream and the outer try block.

    2) If we open 10 objects, each dependent on the previous one, the code would become very complex. Although being new to .net, I already have managed to get into this situation.

    Problem 1) could be partially solved by wrapping each statement in the code into a try block, but that isn't possible in reality.

    So there are two ideas:

    try
      obj_Stream = new filestream(...)
    
      try
        obj_BinReader = new binaryreader(filestream, ...)
        'Some code which does the work
      catch BinReaderEx as SomeSortOfBinaryReaderException
        'Code for handling binaryreader exception
      finally
        obj_BinReader.close
      end try
    
    catch StreamEx as SomeSortOfFileStreamException
      'Code for handling filestream exception
    finally
      obj_Stream.close
    end try

     

    or even

    try
      obj_Stream = new filestream(...)
      obj_BinReader = new binaryreader(filestream, ...)
    
      'Some code which does the work
    
    catch Ex as SomeSortOfExceptionWeWouldLikeToHandle
      'Code for handling all exceptions
    
    finally
      obj_BinReader.close
      obj_Stream.close
        
    end try
    

    Both ensure that obj_BinReader and obj_Stream will be closed in every case, thus solving problem 1). The second one also solves problem 2). But both have a new problem:

    When closing the objects, we don't know if they had been opened correctly. Calling close on an object which had not been opened will cause an exception, of course, but we don't want exceptions in the exception handler ... (I never have tried if we could place a try block into the finally clause of an outer try block, but I wouldn't like it anyway).

    I used to do:

    ...
    finally
      if (not(obj_BinReader is nothing)) then obj_BinReader.close
      if (not(obj_Stream is nothing)) then obj_Stream.close
    ...

    For the C people: nothing means NULL.

    I never have encountered a situation where this behaved unexpectedly. On the other hand, I could not find a single statement in the .net 4.0 docs or elsewhere regarding the return value of the "new" operator / keyword when the object requested could not be generated (i.e. generating the object throws an exception).

    So I am unsure if I am doing it right, and my question is:

    Is there anybody who can give a definitive statement regarding the "return value" of the "new" statement in case of failure? Or is there another more elegant programming principle which avoids this problem completely?

    Thank you very much,

    Peter

     

    • Edited by Binarus Wednesday, November 16, 2011 12:01 PM
    Wednesday, November 16, 2011 11:54 AM

Answers

  • The only reason, why the command fails is an exception. If an exception occurs, then there is no return code / no assignment. The value is nothing/null, because it is defined inside the standards that the CLR has to initialize all memory areas with 0 bytes when allocated.

    With kind regards,

    Konrad

    • Marked as answer by Paul Zhou Thursday, November 24, 2011 8:17 AM
    Thursday, November 17, 2011 9:55 AM

All replies

  • Hi,

    the normal pattern is the last one where you have a finally block in which you check if values are set and then call close.

    This is a pattern know as IDispose or Dispose pattern. The classes implement IDisposable and you should take care that they are disposed (but calling close is also good enough).

    For this, a using statement was introduced (c# and vb, you made vb examples, so I stick to VB in my link, too):
    http://msdn.microsoft.com/en-us/library/htd05whh.aspx

    And another thing that I came across when switching from VS Prof to VS Premium: Code Analysis was complaining when we had such cascaded streams (Sorry, C# now!):

    using (var myStream = new FileStream(...))
    using (var myReader = new BinaryReader(myStream))
    {
      // ...
    }

    This caused a warning of code analysis:
    CA2202: Microsoft.Usage: Object 'myStream' can be disposed more than once in method ......

    And in the end the result for us was code like:

    using (var myStream = new FileStream(...))
    {
      var myReader = new BinaryReader(myStream);
      // ...
    }

    Konrad

    Wednesday, November 16, 2011 12:11 PM
  • Konrad,

    thank you very much for taking the time...

    In fact, I am using both close and dispose (if the respective object exposes them); I first close, then dispose (if possible).

    The link you gave is very interesting, especially where they simulate the "using" keyword by a normal "try" block. Since they claim that the "try" block is equivalent to the "using" code, we can be sure that the method we have discussed is correct.

    But now I would like to know one more thing. Suppose we have the following code:

     

    dim SomeObject as SomeType
    
    try
      SomeObject = new SomeType(...)
    catch
      'Some code here
    finally
      if (not(SomeObject is nothing)) then SomeObject.close
    end try
    

     


    As discussed above, this should be safe, but the reason is still unclear to me. It's clear that SomeObject = nothing if generating the object via "new" failed. But there are two possible reasons (and I would like which of them applies):

    1) After the "dim" statement, SomeObject is initialized to "nothing" (NULL) automatically (i.e. by definition), and the assignment (SomeObject = new ...) just isn't executed if generating the object fails.

    2) Regardless of the possible previous value of SomeObject, "new" will either return an object which succesfully has been generated or nothing (if something went wrong), and the assignment is executed in every case (even if something went wrong with generating the object).

    In other words, I would like to know if the assignment in the forth line of the above code just isn't executed if something goes wrong, or if "new" returns nothing and the assignment is executed.

    Thank you very much,

    Peter

     



    • Edited by Binarus Thursday, November 17, 2011 8:53 AM
    Thursday, November 17, 2011 8:49 AM
  • The only reason, why the command fails is an exception. If an exception occurs, then there is no return code / no assignment. The value is nothing/null, because it is defined inside the standards that the CLR has to initialize all memory areas with 0 bytes when allocated.

    With kind regards,

    Konrad

    • Marked as answer by Paul Zhou Thursday, November 24, 2011 8:17 AM
    Thursday, November 17, 2011 9:55 AM
  • Konrad,

    thank you very much again. That was exactly what I wanted to know.

    With kind regards,

    Peter

     

    Thursday, November 17, 2011 11:44 AM