【问题标题】:async/await and the IDisposable interfaceasync/await 和 IDisposable 接口
【发布时间】:2013-09-17 12:26:11
【问题描述】:

我有一个实现 IDisposable 接口的类来处理私有变量_MailMessage 同一个类有一个使用私有 IDisposable 变量的异步方法,即async public Task<bool> Send 我的问题是:普通的 IDisposable 实现会处理异步方法完成后的私有变量?这是我正在谈论的课程的示例:

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

【问题讨论】:

    标签: c# async-await idisposable


    【解决方案1】:

    除了Email.Dispose 之外,我没有看到您在任何地方明确处置_MailMessage

    async 并没有对IDispose 做任何特别神奇的事情;唯一需要记住的是async 方法可能会提前返回。

    所以如果你这样称呼它:

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

    然后您的调用代码将(异步)等待Send 完成,然后再处理email。但如果你这样称呼它:

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

    那么你的调用代码会在Send完成之前释放email

    【讨论】:

    • 让我有点担心的是,这个语句中使用了私有变量: () => 它会在内存中创建一个副本还是..这是如何工作的?
    【解决方案2】:

    你做的从根本上来说是错误的。要记住的一个好规则是,如果你认为你需要一个析构函数,那么你在 99.9% 的情况下都是错误的。仅当您有需要释放的非托管类型的私有变量时才需要析构函数。你没有。如果 disposing 参数为假,你可以告诉你做错的方式是当你发现你实际上并没有做任何事情时。或者换句话说,析构函数实际上并没有做任何事情。所以不需要。那么你也不需要一次性图案。

    还有更多的错误,你需要继承IDisposable接口来实现你自己的Dispose()方法。你忘了。

    您的 Dispose() 方法需要由创建您的 Email 类实例的客户端代码调用。您不能自己调用​​它,您不知道客户端代码何时停止使用您的 Email 对象。所以这是对您问题的快速回答,您不能在 Send() 方法中处理自己。无法保证客户端代码会实际调用它。您必须将其留给客户端代码才能正确处理。

    【讨论】:

    • 感谢您的回答。如何确定私有变量是否属于非托管类型? IDispose 接口是在 IEmail 接口中继承的,所以我想应该很好。 “你不能自己称呼它”是什么意思。我自己怎么称呼它?
    • 我根据您的回答更改了 IDisposable 实现,有效地删除了析构函数。但是 IDisposable 完全没有必要吗?如果是这样,为什么?
    • 不,没有必要,因为您确实有一个一次性会员。您拥有 MailMessage 对象。
    • 这个类有可能间接继承自IDisposable,尽管IEmail
    猜你喜欢
    • 2012-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-22
    • 1970-01-01
    • 2020-03-25
    • 1970-01-01
    相关资源
    最近更新 更多