After elaborating on the topic on my first post (see comments), it's time to wrap the all thing up. As I've noted, the purpose was to brought up some points of interest about developing classes which wrap unmanaged code inside them. When I'm reviewing my teammates code, any usage of unmanaged code triggers me as I start thinking about all the scenarios of which the unmanaged code stays un-handled and therefore causes memory leaks in the application. One of the scenarios which can cause a memory leak was the one we've talked about(again, the code in the first post was only for teaching purpose, so this post will be more relevant), but this could be better handled by implementing "Dispose pattern" on the (wrapper)class:
public class CustomWriter : IDisposable{ private bool disposed = false; // Track whether Dispose has been called. private StreamWriter m_sw; public CustomWriter(string filePath, int numberOfLinesToWrite) { m_sw = new StreamWriter(filePath); // If we know that an exception may occur after initializing unmanaged code - try-catch is preferable. try { if (numberOfLinesToWrite < 0) { throw new ArgumentException("What's wrong with you ?!!? numberOfLinesToWrite can't be less than 0", "numberOfLinesToWrite"); } // Assume that I'm using numberOfLinesToWrite here... } catch // equals to catch(Exception) { // Explicit disposal of the object - deterministic. Dispose(); throw; // rethrow the exception without losing the stack trace. } } #region IDisposable Members // Implement IDisposable. // Do not make this method virtual ! // A derived class should not be able to override this method. public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); } private void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!disposed) { // If disposing equals true - Explicit disposal is called. // Dispose managed resources. if(disposing) { // clean up managed resources (if have any). } // Call the appropriate methods to clean up unmanaged resources here. m_sw.Close(); disposed = true; } } ~CustomWriter() { // nondeterministic // Do not re-create Dispose clean-up code here. // Calling Dispose(false) is optimal in terms of // readability and maintainability. Dispose(false); } #endregion}
Summing up:
Disclaimer The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.
© Copyright 2009, Oren Ellenbogen
<= Contact me via E-mail
newtelligence dasBlog 2.2.8279.16125