Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will the IDisposable still get disposed if you copy its reference before the disposal?

Consider this:

interface IFoo : IDisposable { }

class Program
{
  static void Main()
  {
    var foo = GetFoo();

    var anotherFoo = foo;

    using(anotherFoo)
    {
    }

    // Will the object on the heap be marked for collection? 
    // Or will this confuse the garbage collector 
    // as we are copying references?
  }
}

That raises the more important question. What does Dispose() actually do?

like image 777
Water Cooler v2 Avatar asked Jan 10 '23 12:01

Water Cooler v2


2 Answers

Dispose method is added in consideration of the fact that resources other than managed memory (unmanaged resources) still need to be released explicitly; GC was specifically not design for it.

Moreover, the mechanism behind IDisposable is independent of the GC. When you follow the Dispose Pattern, you may optionally plug in your code into GC by making your class finalizable, but you do not have to do that as part of implementing IDisposable.

Disposing is related to cleaning up the hidden resources the object holds, not the "shell" of the object inside managed memory. Very often an object would throw ObjectDisposedException on attempts to use it after the call of Dispose.

Going back to your code, since the two variables actually refer to the same object, the foo variable would be referencing a disposed object after the using block. Therefore, calling methods or accessing properties of foo after using may raise an exception:

var foo = GetFoo();

var anotherFoo = foo;

using(anotherFoo)
{
}

foo.doSomethingUseful(); // <<== This may throw ObjectDisposedException!
like image 52
Sergey Kalinichenko Avatar answered Jan 29 '23 12:01

Sergey Kalinichenko


"Copying a reference" as you describe does not do anything to the original object. It's just another way of looking up the same object via a different name -- like if you have a separate email address the mail of which always gets addressed to your mailbox. Either way you get to the same object. Thus if you dispose one then you dispose the other.

When you have the code:

using (anotherFoo) 
{
}

You are using the using syntax to ensure that the last thing that happens before the block finishes is that the .Dispose method on the object referenced by the variable anotherFoo will be disposed. Thus it is exactly the same as if you had done using (foo) { ... }.

Edit Responding to Comment:

First, and most importantly, IDisposable has nothing to do with garbage collection. That happens on its own. In this example, since both foo and anotherFoo are local variables and not (apparently) being escaped from the method via closures, method calls, etc, they will be GC'ed almost as soon as the method call finishes.

IDisposable, on the other hand, is intended purely to allow you to free (generally) external resources such as OS handles like files, UI primitives, sockets, etc. While it's true that you definitely want the object to dispose of it's resources when it's GC'ed, without IDisposable you can't control the inverse -- IDisposable allows you to clean up resources at the very moment when it is not being used anymore. This allows you to engineer more deterministic solutions to problems that deal with external resources -- more than relying purely on what the GC would allow.

like image 41
Kirk Woll Avatar answered Jan 29 '23 11:01

Kirk Woll