2013-09-17 4 views
7

У меня есть класс, который реализует интерфейс IDisposable для размещения частной переменной. _MailMessage. В том же классе есть метод async, который использует переменную private IDisposable, а именно async public Task<bool> Send. Вопрос: Будет ли нормальный Реализация IDisposable удаляет закрытую переменную после завершения асинхронного метода? Вот пример класса я говорю:async/await и IDisableable

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 

} 

Я изменил реализацию IDisposable в соответствии с одним из ответов, которые предлагают не использовать деструктор:

#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 

ответ

10

Вы делаете это довольно принципиально неправильно. Хорошее правило иметь в виду, что если вы думаете, что вам нужен деструктор, тогда вы ошибаетесь в 99,9% случаев. Деструктор требуется только в том случае, если у вас есть приватная переменная неуправляемого типа, которая должна быть выпущена. Вы этого не сделаете. Способ, которым вы можете сказать вам, делает это неправильно, когда вы обнаруживаете, что вы вообще ничего не делаете, если аргумент , содержащий, является ложным. Или, другими словами, деструктор фактически ничего не делает. Так что это не нужно. Тогда вам также не нужен одноразовый шаблон.

Существует больше ошибок, вам нужно наследовать интерфейс IDisposable, чтобы реализовать свой собственный метод Dispose(). Ты забыл.

Ваш метод Dispose() должен быть вызван кодом клиента, который создает экземпляр вашего класса электронной почты. Вы не можете назвать это самостоятельно, вы не знаете, когда клиентский код перестает использовать ваш объект электронной почты. Так что это быстрый ответ на ваш вопрос, вы не можете избавиться от метода Send(). Нет никакой гарантии, что клиентский код действительно назовет ее. Вам нужно будет оставить это до клиентского кода, чтобы все было правильно.

+0

Спасибо за ваш ответ. Как узнать, является ли личная переменная неуправляемым типом? Интерфейс IDispose наследуется в интерфейсе IEmail, поэтому это должно быть хорошо, я думаю. Что вы имеете в виду: «Вы не можете назвать это сами». Где я сам это называю? –

+0

Я изменил реализацию IDisposable в соответствии с вашим ответом, эффективно удалив деструктор. Но является ли IDisposable совершенно ненужным? И если да, то почему? –

+1

Нет, это не лишнее, так как вы * имеете * одноразовый член. У вас есть объект MailMessage. –

14

я не делаю см. в любом месте, где вы явно размещаете _MailMessage, кроме как в Email.Dispose.

async не делает ничего особо магического с IDispose; единственное, что вы должны иметь в виду, это то, что методы async могут вернуться раньше.

Так что, если вы называете это так:

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

Тогда ваш код вызова будет (асинхронно) ждать Send завершить перед утилизацией email. Но если вы называете это так:

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

Затем ваш вызывающий код будет распоряжаться email до Send не завершится.

+0

Что меня беспокоит, так это то, что в этой инструкции используется частная переменная:() => Будет ли она создавать копию в памяти или .. как это работает? –

Смежные вопросы