Casey Chester
Casey Chester

Reputation: 276

How do disposable captured variables get disposed?

Consider the following pseudo code:

public class SomeComponent
{
    private List<DisposableFoo> _foos = new List<DisposableFoo>();

    public void Start()
    {
        for (int i = 0; i < 5; i++)
        {
            var foo = new DisposableFoo();
            var bar = new DisposableBar();                 <-- EXPLICIT DISPOSE NEEDED?
            foo.SomeFunc = x => bar.DoSomethingWithX(x);
            _foos.Add(foo);
        }
    }

    public void Stop()
    {
        _foos.ForEach(f => f.Dispose());
    }
}

Does the infrastructure take care of Disposing any captured IDisposable variables as part of it's tear down?

Clarification: I am not asking about best practices around managing disposable objects. My question is more about what does the infrastructure do in this instance. My understanding is that for capturing variables, behind the scenes the infrastructure creates a type that contains a field of type DisposableBar and it receives a reference to the object in the 'bar' variable. Once the infrastructure has captured that variable it seems like a 'gray area' to me about whose responsibility it is at that point to determine when the variable is no longer needed and can be disposed.

Upvotes: 1

Views: 1502

Answers (3)

Michael Puckett II
Michael Puckett II

Reputation: 6749

The short answer is yes most definitely. You need to call dispose on any object that is disposable. This is used to clean up unmanaged resources and dependencies.

Also NOTE: The garbage collector does not call Dispose on or look for IDisposable types.

If it is disposable within the method then it is ideal to use a using statement like so.

public void Start()
{
    for (int i = 0; i < 5; i++)
    {
        using (var foo = new DisposableFoo())
        using (var bar = new DisposableBar())
        {
             foo.SomeFunc = x => bar.DoSomethingWithX(x);
             _foos.Add(foo);
        }
    }
}

If the variable is class level then your class should also implement IDisposable and dispose of the disposable objects it uses within it as well.

Here is a good link where I give more detail about disposing objects.

Another thing to keep in mind is that sometimes (in languages like C#) we can have circular dependencies (which is bad practice.) However; it happens a lot. If your object is garbage collected and there is a circular dependency it hangs around until the other object is also garbage collected and it causes the process to be ugly. It's all behind the scenes and usually isn't a big deal for most apps but being aware of this is important. Although you should not have circular dependencies you can implement IDisposable to clean up dependencies before going to the garbage collector, making this process cleaner. (Just remember having them is bad to start with... That said, Entity Framework is built on circular dependencies so go figure.)

Another NOTE: It is not uncommon to see the Dispose method added also to the destructor of an object; especially if the object is lower level, singleton, or static, to insure the disposable types are taken care of during garbage collection. This would look something like:

public class SomeClass : IDisposable
{
     //pretend we implement a singleton pattern here
     //pretend we implement IDisposable here
    ~SomeClass()
    {
        Dispose(); 
    }
}

UPDATE:

To update the answer based on your clarification I believe you're asking what happens to the variable you retrieve from a disposable object, after that disposable object is disposed. This is tricky behavior and should be thought out well when developing a disposable type. Here's some code that shows the results of similar situation that might help you understand. Also. whose responsible for this should be decided when developing the type but in most cases any information you give to the client should be left good for the client even after you're disposed. In other words, it would be best practice to NOT remove or dispose or manipulate any information you allow the user of your type to retrieve when you are disposing.

using System;
using System.Collections.Generic;
namespace Disposable_Variables_Reference
{
    class Program
    {
        static void Main(string[] args)
        {
            List<string> DCNames = null;
            string DCName = string.Empty;
            int DCValue;
            using (var disposableClass = new DisposableClass())
            {
                DCNames = disposableClass.Names;
                DCName = disposableClass.Name;
                DCValue = disposableClass.Value;
                foreach (var name in DCNames) Console.WriteLine(name);
                Console.WriteLine(DCName);
                Console.WriteLine(DCValue);
            }
            foreach (var name in DCNames) Console.WriteLine(name);
            Console.WriteLine(DCName);
            Console.WriteLine(DCValue);
            Console.Read();
        }

        public class DisposableClass : IDisposable
        {
            public List<string> Names { get; set; } = new List<string>() { "Michael", "Mark", "Luke", "John" };
            public string Name { get; set; } = "Gabriel";
            public int Value { get; set; } = 20;
            public void Dispose()
            {
                Names.Clear();
                Name = string.Empty;
                Value = 0;
            }
        }
    }
} 

Output:

Michael
Mark
Luke
John
Gabriel
20
Gabriel
20

Names is a List (reference type) and IS NOT re-written to the output.

Name is string (immutable reference type) and IS re-written to the output.

Value is int (value type) and IS re-written to the output.

However; if you reassign Names in the Dispose() method, instead of clearing it, then it WILL ALSO be re-written. Example including just the dispose method change.

public void Dispose()
{
    Names = null; //notice here we re-assign Names to null.
    Name = string.Empty;
    Value = 0;
}

New Output:

Michael
Mark
Luke
John
Gabriel
20
Michael
Mark
Luke
John
Gabriel
20

Knowing this the proper way to expose Names would be to leave it alone in the Dispose() method or expose name like so; returning a new list, so that any referendes to it are not removed when disposing.

private List<string> names = new List<string>() { "Michael", "Mark", "Luke", "John" };
public List<string> Names
{
    get { return names.ToList() ; }
    set { names = value; }
}

Of course this entire answer is for logic and clarification. There is no reason to use IDisposable in the DisposableClass example I've given.

Upvotes: 2

programtreasures
programtreasures

Reputation: 4298

If you have used unmanaged code in DisposableBar than it needed to be disposed, else Garbage collator will take care managed resources.

Upvotes: 0

Stilgar
Stilgar

Reputation: 23571

Yes and no. The proper thing to do will be to dispose manually. If this code is similar to the real app you should gather the bars in a list and dispose them after you dispose the foos. Depending on how your real code is structured you might need another strategy. If the bar is unmanaged resource you should always dispose it. In most cases the unmanaged resource is wrapped in a managed resource for example StreamReader wraps an unmanaged file handle. In these cases the object will be disposed when the managed object is garbage collected assuming that the dispose pattern is implemented correctly. The problem is that the GC is not deterministic and will run when there is memory pressure. There might be a situation where GC does not run but your app is starved for file handlers but since GC only cares about memory it does not run and the unmanaged resource is not disposed.

Upvotes: -1

Related Questions