Best way to dispose a list

Viewed 98265

I am having List object. How can I dispose of the list?

For example,

List<User> usersCollection =new List<User>();

User user1 = new User();
User user2 = new User()

userCollection.Add(user1);
userCollection.Add(user2);

If I set userCollection = null; what will happen?

foreach(User user in userCollection)
{
    user = null;
}

Which one is best?

19 Answers

I don't agree that you shouldn't do anything if you don't need the objects in the list anymore. If the objects implement the interface System.IDisposable then the designer of the object thought that the object holds scarce resources.

If you don't need the object anymore and just assign null to the object, then these scarce resources are not freed until the garbage collector finalizes the object. In the mean time you can't use this resource for something else.

Example: Consider you create a bitmap from a file, and decide you don't need neither the bitmap, nor the file anymore. Code could look like follows:

using System.Drawing;
Bitmap bmp = new Bitmap(fileName);
... // do something with bmp until not needed anymore
bmp = null;
File.Delete(fileName); // EXCEPTION, filename is still accessed by bmp.

The good method would be:

bmp.Dispose();
bmp = null;
File.Delete(fileName);

The same accounts for objects in a list, or any collection. All objects in the collection that are IDisposable should be disposed. Code should be like:

private void EmptySequence (IEnumerable sequence)
{   // throws away all elements in the sequence, if needed disposes them
    foreach (object o in sequence)
    {
        // uses modern pattern-matching
        if (disposableObject is IDisposable disposable)
        {
            disposable.Dispose();
        }
    }
}

Or if you want to create an IEnumerable extension function

public static void DisposeSequence<T>(this IEnumerable<T> source)
{
    foreach (IDisposable disposableObject in source.OfType(System.IDisposable))
    {
        disposableObject.Dispose();
    };
}

All lists / dictionaries / read only lists / collections / etc can use these methods, because they all implement IEnumerable interface. You can even use it if not all items in the sequence implement System.IDisposable.

Many of these answers have something like...

public static void DisposeAll(this IEnumerable clx) {
    foreach (Object obj in clx) 
    {
        IDisposable disposeable = obj as IDisposable;
        if (disposeable != null) 
            disposeable.Dispose();
    }
}

usersCollection.DisposeAll();
usersCollection.Clear();

There's not a single answer which mention of why .Clear() is helpful. The answer is decoupling the items in the collection from the both the collection and each other.

The more you decouple on tear down of any object the greater the chance the garbage collector will do it's job in a timely manner. Often substantial .NET memory leaks result from large graphs of objects which are 99% unused which have one item that is still being referenced.

I think its good practice to...

  • unsubscribe from any events that the object is subscribed to
  • clear any collections held
  • and set all the members to null.

...in a class implementing IDisposable.

I'm not suggesting implementing IDisposable on everything and doing this, I'm saying if it so happens that you need to implement dispose you might as well do it. I only implement IDisposable when the object...

  • subscribe to events or
  • owns members which implement Dispose() such as a DB connection or a Bitmap or
  • has other unmanaged resources.

The only time I would only implement Dispose just to decouple an object would be when you know you have a memory leak and the analysis of a memory profiler suggested it might be helpful.

If your item in list is un-managed object then you can call Dispose() on every object by iterating it.

foreach(User user in userCollection)
{
user.Dispose();
}

If the list object is managed object then you need not do anything. GC will take care.

This might help someone :

public class DisposableList<T> : List<T>, IDisposable where T : IDisposable
{
    public void Dispose()
    {
        foreach (var item in this)
            item?.Dispose();
    }
}

or simpler :

public class DisposableList : List<IDisposable>, IDisposable
{
    public void Dispose()
    {
        foreach (var item in this)
            item?.Dispose();
    }
}

I think a smarter way of doing this is to create a custom scope.

   public sealed class DisposableScope : IDisposable
   {
      // you can use ConcurrentQueue if you need thread-safe solution
      private readonly Queue<IDisposable> _disposables = new();

      public T Using<T>(T disposable) where T : IDisposable
      {
         _disposables.Enqueue(disposable);
         return disposable;
      }

      public void Dispose()
      {
         foreach (var item in _disposables)
            item.Dispose();
      }
   }

e.g usage :

create and dispose of multiple files (it can be any disposable object though)

using var scope = new DisposableScope();

foreach (var fileName in files)
{
   var file = scope.Using(File.Create(fileName));
   
   // some other action
}

OurDisposableScope can safely handles everything.

Related