Question and Answer on Non-Deterministic Finalization
Always close files and streams

Question:

I read your article Manage C# Objects in Visual Studio magazine.

I'm having a problem using the XmlDataDocument class and was wondering if you think this is a situation where I should use IDisposable as described in your article.  The way the form works is the user will make changes and then click a "Save" button, or other events will trigger a save, which should persist changes to the XML file.

Thanks in advance for your help.

class frmMain : System.Windows.Forms.Form
{
  //code omitted

  string xmlSchema = (@"C:\Projects.xsd");
  string xmlFile = (@"C:\Projects.xml");
  XmlDataDocument xdd = new XmlDataDocument();

  public frmMain()
  {
    InitializeComponent();
    xdd.DataSet.ReadXmlSchema(xmlSchema);
    // load xml data
    XmlTextReader xrd = new XmlTextReader(xmlFile);
    xrd.MoveToContent();
    xdd.Load(xrd);
  }
 
 private void btnAddCfg_Click(object sender, System.EventArgs e)
 {                
   DataTable cfgTbl = xdd.DataSet.Tables["Configuration"];
   DataRow newCfg = cfgTbl.NewRow();
   newCfg["ConfigName"] = "NewConfiguration";
   cfgTbl.Rows.Add(newCfg);
   xdd.Save(xmlFile); // ç Error here: "The process cannot access the
   // file "C:\Projects.xml" because it is being used by
   // another process."
 }
}

Answer: Well, the answer is rather simple:  You never closed the file. When the constructor exits, the XmlTextReader is garbage. Eventually, the .NET Garbage Collector will reclaim that memory.  However, before the GC can reclaim the memory, it will call the finalizer for the XmlTextReader.  That will close the file.  If you wait long enough, the file does get closed.  Long enough is a non-deterministic amount of time.

To fix the problem, you need to explicitly close the file.  You also need to make sure that you close the file, even if an exception gets thrown in your constructor.  That means a using clause, or a finally clause. Your constructor should look like this:

public frmMain()
{
  InitializeComponent();
  xdd.DataSet.ReadXmlSchema(xmlSchema);
  // load xml data
  XmlTextReader xrd = null;
  try {
    xrd = new XmlTextReader(xmlFile);
    xrd.MoveToContent();
    xdd.Load(xrd);
  } finally
  {
    if ( xrd != null )
      xrd.Close( );
  }
}

You can’t put the XmlTextReader in a using clause, because it does not support IDisposable, even though it has a Close() method.

Changing it in this manner ensures that the file is always closed before the constructor exits.  That’s all there is to it.

I hope that helps.  Follow the link below for much more on disposing and closing objects (from my new book, Effective C#)



Implement the Standard Dispose Idiom
Not exactly what he's seeking, but close
The original aticle
The article referenced in the question
Created: 12/8/2004 6:47:29 PM

Current Projects

I create content for .NET Core. My work appears in the .NET Core documentation site. I'm primarily responsible for the section that will help you learn C#.

All of these projects are Open Source (using the Creative Commons license for content, and the MIT license for code). If you would like to contribute, visit our GitHub Repository. Or, if you have questions, comments, or ideas for improvement, please create an issue for us.

I'm also the president of Humanitarian Toolbox. We build Open Source software that supports Humanitarian Disaster Relief efforts. We'd appreciate any help you can give to our projects. Look at our GitHub home page to see a list of our current projects. See what interests you, and dive in.

Or, if you have a group of volunteers, talk to us about hosting a codeathon event.