这是C#'关闭'在参数引用中仍然有效吗?

时间:2015-10-22 12:53:45

标签: c#

让我们看一下.NET SmtpClient的一个简单案例,您可以使用它来执行以下操作(忽略凭据,SSL和异常处理):

async Task sendMail()
{
    var m = new MailMessage(...);
    var s = new SmtpClient(host, port);

    s.SendCompleted += (s, e) => { m.Dispose(); s.Dispose(); }; 

    await s.SendMailAsync(m);
}

一切都很好;每当异步发送完成时,将调用执行Dispose()的func。 m和s都在声明它的包含范围内,所以没有问题。

但是说我们有以下内容:

SmtpClient setupMailClient(MailMessage mail)
{
    var smtpClient = new SmtpClient(host, port);

    smtpClient.SendCompleted += (s, e) => { mail.Dispose(); smtpClient.Dispose(); };

    return smtpClient;
}

async Task sendMail()
{
    var m = new MailMessage(...);
    var s = setupMailClient(m);

    await s.SendMailAsync(m);
}

现在,调用await sendMail()仍然是安全的吗?封闭仍然有效吗?我们将对MailMessage的引用传递给邮件' - 这是引用的副本,仅在setupMailClient()的上下文中有效。我是否在思考它,或者这仍然是安全使用的?

3 个答案:

答案 0 :(得分:3)

两个程序的行为都相同。第二个程序更令人困惑,因为sendMail是创建MailMessage的方法,所以它是负责处理它的人(按惯例)。它没有处理对象,它调用的方法可能会使其他人阅读代码时感到困惑。在这种情况下,您应该sendMail处置MailMessage(这可以通过将其包装在using中来完成。

  

我们将对MailMessage的引用传递给' mail' - 这是引用的副本,仅在setupMailClient()的上下文中有效。

仅在setupMailClient的上下文中有效。它是该对象整个生命周期的有效参考。它只是setupMailClient上下文中范围内的,这意味着只允许该方法中的代码按名称引用该参数。 Scope and lifetime are very different conceptsmail的范围只是那个方法,但是该变量的生命周期可以(并且在这种情况下)比该方法返回(由于闭包)更长。

答案 1 :(得分:2)

是的,它仍然是安全的。在方法的上下文中MailMessage mail没有改变意义(没有迭代一次又一次地重置它)。

这意味着您的代码使用安全且封闭完好无损。 (你确实知道你最好在这些情况下使用using,对吗?)

答案 2 :(得分:1)

捕获变量是因为它是函数的参数。所以它不能从函数外部改变(除了对象的状态)。 SmtpClient也是一个局部变量,因此与邮件的范围相同。