Phantom CLR-objects with X++

Some weeks ago I got the following code:

    1:  try 
    2:  { 
    3:      iop = new InteropPermission(InteropKind::ClrInterop); 
    4:      iop.assert(); 
    5:   
    6:      fileInfo = new System.IO.FileInfo(filename); 
    7:   
    8:      if (WinAPI::fileExists(filename)) 
    9:      { 
   10:          fileInfo.Delete(); 
   11:      }  
   12:      fileInfo.Create(); 
   13:  } 
   14:  catch 
   15:  { 
   16:      throw error(strfmt('@SYS72726', _filename)); 
   17:  } 

The programmer told me, that a file, created with this code, could not be deleted. He had to stop the application in order to delete the files.

Well, what happened here?

The Create() returns a System.IO.FileStream-object. In this code there is no variable that holds a reference to this FileStream-object, so you would think that this object will be destroyed at least once this method terminates. Unfortunately this isn't the case. Even weeks after the last execution, the FileStream-instance hasn't been collected by the garbage collector (GC). Obviously, somewhere inside Dynamics Ax remains a reference to this object. The only way to destroy this FileStream-object (or better to let the GC destroy this object), is by shutting down the application (the Ax-client or the AOS - it depends where this code has been executed).

If you now declare a variable that holds the reference to the FileStream-object, this object will be disposed after the execution of this method (when it leaves the scope) as you already expected it for the code above. The behavior changes completely just by having now a variable that holds the reference. 

So in X++ you have to pay attention to all objects that are returned by methods of CLR-classes, since they are not disposed as they should be or at least when you expects them to be disposed. In order to prevent bugs (as in the code above) you must explicitly declare variables that hold the reference to these objects.

The following code did resolve the problem:

    1:  System.IO.StreamWriter sw ;
    2:   
    3:  ...
    4:   
    5:  try 
    6:  { 
    7:      iop = new InteropPermission(InteropKind::ClrInterop); 
    8:      iop.assert(); 
    9:   
   10:      fileInfo = new System.IO.FileInfo(filename); 
   11:   
   12:      if (WinAPI::fileExists(filename)) 
   13:      { 
   14:          fileInfo.Delete(); 
   15:      }  
   16:      sw  = fileInfo.Create();
   17:   
   18:      sw.Dispose(); //this isn't necessary to resolve the problem but you should call the dispose nevertheless.
   19:  } 
   20:  catch 
   21:  { 
   22:      throw error(strfmt('@SYS72726', _filename)); 
   23:  } 

 

UPDATE (12/08/2008): This behaviour can be reproduced under Ax2009.