List class/controller to prevent memory exhaustion In my Outlook spam add-on when I receive a new email I create a new task to process the email.

That way I don't block Outlook itself.

To prevent the List collection from growing exponentially, (traditionally Outlook is not closed for a long, long time), I have created a simple TasksController class that will help me keeping the list to a manageable size.

Then I can simply call:

TasksController.Add( ProcessNewEmail() ) 
... 
private async Task&lt;bool&gt; ProcessNewEmail()
{
   /// ... do stuff
}


This is the class itself

internal class TasksController : IDisposable
{
  private static TasksController _controller;

  private static TasksController Controller =&gt; _controller ?? (_controller = new TasksController());

  private readonly object _lock = new object();
  private readonly List&lt;Task&gt; _tasks;

  private TasksController()
  {
    _tasks = new List&lt;Task&gt;();
  }

  public void Dispose()
  {
    WaitAll();
  }

  private void _Add( Task run )
  {
    //  remove all the completed tasks
    RemoveAllCompleted();

    // and add the new task
    lock (_lock)
    {
      _tasks.Add(run);
    }
  }

  private void _WaitAll()
  {
    lock (_lock)
    {
      Task.WaitAll(_tasks.ToArray());
    }
    RemoveAllCompleted();
  }

  private void RemoveAllCompleted()
  {
    lock(_lock)
    {
      _tasks.RemoveAll(t =&gt; t.IsCompleted);
    }
  }

  public static void WaitAll()
  {
    Controller._WaitAll();
  }

  public static void Add(Task run)
  {
    Controller._Add( run );
  }
}


Is deriving from IDisposable necessary?
Is the class itself thread safe?
Is the class itself thread safe

Looks fairly safe except TasksController Controller part.

Firstly, I would suggest to use .NET classes for storing concurent data instead of List&lt;T&gt; depending on your needs you can use ConcurrentBag&lt;T&gt;, ConcurrentQueue&lt;T&gt;. 
Secondly, create lock for TasksController Controller and use it inside getter. Something like this: 

private static TasksController Controller {
    get{
        lock(_controllerLock){ 
           if(_controller == null){ (_controller = new TasksController();} 
           return _controller
        }
    }}


Is deriving from IDisposable necessary

I would say: no. All tasks should be disposed automatically when they finish.

However, there is only one but, you might to cancell your tasks when object is being disposed. That means if they do some heavy calculations which you do not need anymore after main object disposes. See this answer for information how to do that. 

For more information about disposing tasks see MSDN blog post