Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

async/await and the IDisposable interface

I have a class which implements the IDisposable interface to dispose a private variable _MailMessage The same class has a async method that makes use of the private IDisposable variable, namely async public Task<bool> Send My question is: Will the normal IDisposable implementation dispose the private variable after the async method completes? Here is an example of the class I am talking about:

public class Email : IEmail
{
    private readonly IEmailData _EmailData;
    private MailMessage _MailMessage = new MailMessage();

    public Email(IEmailData emailData)
    {
        if (emailData == null)
        {
            throw new ArgumentNullException("emailData");
        }
        if (String.IsNullOrEmpty(emailData.To))
        {
            throw new ArgumentNullException("emailData.To");
        }
        if (String.IsNullOrEmpty(emailData.From))
        {
            throw new ArgumentNullException("emailData.From");
        }
        if (String.IsNullOrEmpty(emailData.FromName))
        {
            throw new ArgumentNullException("emailData.FromName");
        }
        if (String.IsNullOrEmpty(emailData.Subject))
        {
            throw new ArgumentNullException("emailData.Subject");
        }
        if (String.IsNullOrEmpty(emailData.Body))
        {
            throw new ArgumentNullException("emailData.Body");
        }

        _EmailData = emailData;
    }


    async public Task<bool> Send()
    {
        return await Task.Run<bool>(() =>
        {
            using (SmtpClient smtp = new SmtpClient())
            {
                smtp.Send(_MailMessage);
            }
            return true;
        });
    }

    #region "IDisposable implementation"
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }


    ~Email()
    {
        Dispose(false);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_MailMessage != null)
                _MailMessage.Dispose();
        }
    }
    #endregion

}

I have changed the IDisposable implementation according to one of the answer that suggest not to use a destructor:

#region "IDisposable implementation"
public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_MailMessage != null)
            _MailMessage.Dispose();
    }
}
#endregion
like image 700
Kees de Wit Avatar asked Sep 17 '13 12:09

Kees de Wit


1 Answers

I don't see anywhere you're explicitly disposing _MailMessage other than in Email.Dispose.

async doesn't do anything particularly magical with IDispose; the only thing you have to keep in mind is that async methods may return early.

So if you call it like this:

using (var email = new Email(...))
{
  await email.Send();
}

Then your calling code will (asynchronously) wait for Send to complete before disposing email. But if you call it like this:

Task task;
using (var email = new Email(...))
{
  task = email.Send();
}

Then your calling code will dispose email before Send completes.

like image 91
Stephen Cleary Avatar answered Nov 02 '22 00:11

Stephen Cleary