Question

What happens if you instantiate an object that implements IDisposable during a method call?

For example

return MyMethod(new MyIDisposableObject());

Will the Dispose method of MYIDisposableObject ever get called?

Ok, so if I have the following code in MyIDisposableObject will the IDBConnection get closed and disposed of properly or is it still not safe?

protected IDbConnection _db;
    bool _disposed;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~MyIDisposableObject()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
        {
            // free other managed objects that implement
            // IDisposable only
            if (_db != null)
            {
                _db.Close();
                _db.Dispose();
            }
        }

        // release any unmanaged objects
        // set the object references to null
        _db = null;

        _disposed = true;
    }
Was it helpful?

Solution

Dispose() is a normal method.
Like any other method, it will not be called unless you write code that calls it.

For example, the using() statement generates code that calls the Dispose() method.

Note that classes that own native resources are supposed to have a finalizer that calls Dispose(false) to release them (see the Dispose() pattern).
The finalizer will run once the object is GC'd (which may never happen)

OTHER TIPS

Not by default. Here's a demonstrative sample. You will see that the Cat never got disposed.

class Program
{
    public static void Main(string[] args)
    {
        SayCatName(new Cat() { Name = "Whiskers" });
        Console.Read();
    }
    public static void SayCatName(Cat c)
    {
        Console.WriteLine(c.Name);
    }
}
public class Cat : IDisposable
{
    public string Name { get; set; }
    public void Dispose()
    {
        Console.WriteLine("Cat was disposed");
    }
}

Unless MyMethod calls the Dispose() method of its parameter, no, it will not. And it's not a great pattern to do. Let the code which owns the resource dispose the resource. Your code should be more idiomatically written as:

using (var o = new MyIDisposableObject())
{
    return MyMethod(o);
}

Unlike C++, where the lifetime an object and the cleanup of its resources are closely connected, in .NET they are largely detached. Objects in .NET live as long as the system "knows" about them, or a reference to them is held in some other object the system knows about. Objects cease to exist when the last reference to them is overwritten. Although the system keeps "hidden" references to certain objects (e.g. it has a list of all objects which have registered Finalize methods), in many cases the only evidence that some particular object ever existed will be a user-code reference to that object. If there is e.g. a 1024-byte range of memory which is not used by anything the GC knows about, the GC will neither know nor care whether that space had been held by sixteen 64-byte objects, a dozen 84-byte objects and a 16-byte object, or some other combination of objects.

This approach works very well for managing memory. The one problem with it comes when objects ask other entities to do things (like grant exclusive access to a file) until further notice. If an object which asks for exclusive access to a file simply ceases to exist without letting anyone know such access is no longer needed, the file will by needlessly inaccessible to everyone else. The IDisposable interface resolves this problem, somewhat: an object whose Dispose method is called should notify every entity that has asked to do anything its behalf until further notice, that it has no further need of such services.

The key to properly using IDisposable is to ensure that every object which requires cleanup has at any given time exactly one "owner". That owner should either be a local variable which is guarded via using or try/finally block or a field of an object which implements IDisposable, and which will Dispose the field when its own Dispose method is called. If a method with a local variable which owns an IDisposable returns that variable without having called Dispose, then ownership will be transferred to the caller of the method. Note that C# has no linguistic construct to recognize ownership except when an object which is created within a method will not be needed after the method returns (a using block handles that case nicely). Otherwise, it is necessary for programmers to keep track of object ownership manually. Failure to do so won't cause compilation errors, but will often cause programs not to work, or to leave outside entities waiting needlessly for notice that their services are no longer required.

The case of return new MyIDisposableObject(); doesn't quite meet either of the above stated patterns, but is nonetheless acceptable because if it were guarded by a try/finally, it would look like:

bool ok = false;
MyIDisposableObject ret = null;
try
{
  ret = new MyIDisposableObject();
  ok = true;
  return ret;
}
finally
{
  if (!ok && ret != null)
    ret.Dispose();
}

The only way the ret.Dispose() statement could execute would be if an exception occurred sometime between the store to ret and the following return. If code is written as return new MyIDisposableObject();, there's no way an exception can occur there.

Your code, however, is different, since you add another function call. That pattern is only safe, however, if MyMethod promises to either return an object which encapsulate the passed-in object, or Dispose it if unable to do so because of an exception. Since that is generally not the case, depending upon whether MyMethod is supposed to return an object which encapsulate the passed-in reference, the correct pattern would either be:

using (var myObject = new MyDisposableObject())
  return MyMethod(myObject);

if the MyDisposableObject will not be encapsulated in the object returned by MyMethod, and will thus have no further use once it returns, or else

MyIDisposableObject innerObject = new MyDisposableobject;
try
{
  var ret = MyMethod(innerObject);
  innerObject = null; // Note that `ret` still has an encapsulated reference to it
  return ret;
}
finally
{
  if (innerObject != null) // Reference wasn't yet encapsulated in `ret`
    innerObject.Dispose();
}

Note that if the call to MyMethod succeeds, the innerObject variable will be cleared but the object won't told to give outside entities notice to discontinue their services, since the calling code will need the object returned by MyMethod, which will in turn need innerObject, which will in turn need those outside entities' services. If the call to MyMethod throws an exception, then the innerObject variable will not be cleared, and the finally block code will thus know that it holds the only reference to innerObject, that reference is about to vanish, and thus no other code will ever use innerObject. Consequently, the finally block needs to ask innerObject to immediately notify outside entities their services are no longer required; , if it doesn't do so nothing else will.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top