【问题标题】:C# how to correctly dispose of an SmtpClient?C# 如何正确处理 SmtpClient?
【发布时间】:2011-02-16 09:20:33
【问题描述】:

VS 2010 代码分析报告如下:

警告 4 CA2000:Microsoft.Reliability:在方法“Mailer.SendMessage()”中,对象“client”并未沿所有异常路径进行处理。在对对象“客户端”的所有引用超出范围之前调用 System.IDisposable.Dispose。

我的代码是:

public void SendMessage()
    {
        SmtpClient client = new SmtpClient();

        client.Send(Message);
        client.Dispose(); 
        DisposeAttachments(); 
    }

我应该如何正确处置客户?

更新:回答 Jons 的问题,这里是处理附件功能:

private void DisposeAttachments()
{
    foreach (Attachment attachment in Message.Attachments)
    {
        attachment.Dispose();
    }
    Message.Attachments.Dispose();
    Message = null; 
}

上次更新完整的课程列表(简短)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Mail;

public class Mailer
{
    public MailMessage Message
    {
        get;
        set;
    }

    public Mailer(MailMessage message)
    {
        this.Message = message; 
    }

    public void SendMessage()
    {
        using (SmtpClient client = new SmtpClient())
        {
            client.Send(Message);
        }
        DisposeAttachments(); 
    }

    private void DisposeAttachments()
    {
        foreach (Attachment attachment in Message.Attachments)
        {
            attachment.Dispose();
        }
        Message.Attachments.Dispose();
        Message = null; 
    }
}

【问题讨论】:

  • @JL - 而不是手动处理附件,您应该处理邮件本身,它会处理附件、备用视图和其他部分。
  • 正确,但在旧版本中(.net 4.0 之前)SmtpClient 没有实现 Dispose 方法

标签: c# .net-4.0


【解决方案1】:
public void SendMessage()
{
    using (SmtpClient client = new SmtpClient())
    {
        client.Send(Message);
    }
    DisposeAttachments(); 
}

这样,即使在Send 方法调用期间抛出异常,客户端也会被释放。您应该很少需要显式调用Dispose - 它应该几乎总是在using 语句中。

但是,不清楚附件是如何在这里涉及的。你的班级是否实现了IDisposable 本身?如果是这样,那可能是处理可能是成员变量的附件的地方。如果您需要绝对确保它们在这里得到处理,您可能需要:

public void SendMessage()
{
    try
    {
        using (SmtpClient client = new SmtpClient())
        {
            client.Send(Message);
        }
    }
    finally
    {
        DisposeAttachments(); 
    }
}

【讨论】:

  • @JL: 嗯...感觉附件应该作为 instance 的一部分进行处理,可能。您的DisposeAttachments 消息也意味着您之后无法收到该消息,这听起来有点奇怪......
  • 一旦发送消息,我不需要实例来发送消息或附件。我应该实现一个析构函数吗?
  • SmtpClient 没有实现 IDisposable :-)
  • JFYI:请记住,如果与 smtp-server 的连接断开,则在调用 .Dispose() 时有时会抛出异常,而不是 .Send()。因此,将整个 using 块保留在 try-catch 中,而不仅仅是 Send() 调用。
  • @JonSkeet 当然你是对的。只是想强调,如果您想捕获连接错误 - 将“dispose()”调用保留在 TRY 块内。这不是人们所期望的。
【解决方案2】:

.NET 4.0 中的 SmtpClient 类现在实现了 IDisposable,而 .NET 2.0 中的 SmtpClient 类缺少此接口(如 Darin 所述)。这是框架中的一项重大更改,您应该在迁移到 .NET 4.0 时采取适当的措施。但是,在迁移到 .NET 4.0 之前,可以在代码中缓解这种情况。这是一个例子:

var client = new SmtpClient();

// Do not remove this using. In .NET 4.0 SmtpClient implements IDisposable.
using (client as IDisposable)
{
    client.Send(message);
} 

此代码将在 .NET 2.0(+3.0 和 3.5)和 .NET 4.0 下正确编译和运行。

【讨论】:

  • 但是……它对实际正确处理客户端有帮助吗?
  • 因为早期 3.5 版本的真正问题是它没有向服务器发送 QUIT 消息,导致服务器一直等待通信。这显然会在重新连接到同一服务器时导致问题。
  • @Nyerguds:这个结构当然不能解决这个问题。
  • 顺便说一下,虽然这会编译,但您必须记住,“as”是一个安全的转换,在失败的情况下返回 null。意思是,它肯定会在 4.0 之前编译,但如果强制转换失败,它可能会在此时使 client 为 null,然后立即导致 null 引用错误。
  • @Nyerguds:与其想象,你为什么不测试它,就像我在写下这个答案之前所做的那样?
【解决方案3】:
using (SmtpClient client = new SmtpClient())
{
    client.Send(Message);
    DisposeAttachments(); 
}

有趣——与.NET 3.5相反,SmtpClient在.NET 4.0中实现IDisposable,每天都在学习新东西。

【讨论】:

  • @Darin 的主要好处是 SMTP 客户端现在在处理期间最终发送 QUIT 命令 :) 对此非常高兴!
  • @Darin:WTF?? SmtpClient 在 .NET 4.0 中实现 IDisposable ???这是一个相当大的突破性变化。这很痛。
  • @Steven,是的,它似乎终于通过向服务器发送 QUIT 命令正确关闭了连接。
  • 啧,啧,啧,IDisposable滥用。 System.Net 团队没有羞耻心。
  • 请注意 - 如果您没有使用网络传递方法,并且您没有设置 Host 属性,那么 Dispose 方法将抛出 InvalidOperationException! connect.microsoft.com/VisualStudio/feedback/details/539160/…
【解决方案4】:

我会这样做:

class Attachments : List<Attachment>, IDisposable
{
  public void Dispose()
  {
    foreach (Attachment a in this)
    {
      a.Dispose();
    }
  }
}

class Mailer : IDisposable
{
  SmtpClient client = new SmtpClient();
  Attachments attachments = new Attachments();

  public SendMessage()
  {
    [... do mail stuff ...]
  }

  public void Dispose()
  {
    this.client.Dispose();
    this.attachments.Dispose();
  }
}


[... somewhere else ...]
using (Mailer mailer = new Mailer())
{
  mailer.SendMail();
}

如果您想发送多封邮件,这将允许重用 SmtpClient 对象。

【讨论】:

    【解决方案5】:

    这是通过代码警察测试的更简洁的解决方案(如果发送失败,将始终调用 dispose):

    public void SendMessage()
    {
        using (SmtpClient client = new SmtpClient())
        {   
            client.Send(Message);
            DisposeAttachments(); 
        }
    }
    

    【讨论】:

      【解决方案6】:

      我最初的问题是间歇性发送失败。 例如。第一个 Send() 成功,第二个 Send() 失败,第三个 Send() 成功。 起初我以为我没有正确处理。 于是我求助于using()

      反正后来我加了UseDefaultCredentials = false,Send()终于稳定了。我仍然保留了using() 功能。

      【讨论】:

        【解决方案7】:
        public void SendMessage()
        {
            try
            {
                using (SmtpClient client = new SmtpClient())
                {
                    client.Send(Message);
                client.dispose()
        
                }
            }
            finally
            {
                DisposeAttachments(); 
            }
        }
        

        【讨论】:

        • 请尝试提供您的代码唯一答案和一些描述
        • using 块内调用 Dispose() 似乎完全是多余的。
        猜你喜欢
        • 2010-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-03-26
        • 1970-01-01
        • 2017-01-31
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多