Reputation: 1330
Let's take a simple case with the .NET SmtpClient, with which you can do the following (ignoring credentials, SSL and exception handling):
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);
}
All fine; the func to do the Dispose()ing will be called whenever the async sending finishes. The m and the s are within the containing scope where it was declared, so no problem there.
But say we had the following:
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);
}
Now, will calling await sendMail() still be safe? Is the closure still valid? We pass in the reference to the MailMessage as 'mail' - which is a copy of the reference, only valid in the context of setupMailClient(). Am I overthinking it, or is this still safe usage?
Upvotes: 0
Views: 486
Reputation: 203847
Both programs will behave identically. The second program is much more confusing to follow, in that sendMail
is the method creating the MailMessage
, so it is the one responsible for disposing of it (by convention). That it doesn't dispose of the object and a method it calls does is likely to confuse others reading the code. You should really have sendMail
dispose of the MailMessage
in that case (which can trivially be done by just wrapping it in a using
.
We pass in the reference to the MailMessage as 'mail' - which is a copy of the reference, only valid in the context of setupMailClient().
It's not only valid in the context of setupMailClient
. It's a valid reference for the entire lifetime of that object. It's only in scope in the context of setupMailClient
, meaning only code within that method is allowed to refer to that parameter by name. Scope and lifetime are very different concepts. The scope of mail
is just that method, but the lifetime of that variable can be (and in this case is) much longer than just until that method returns (due to the closure).
Upvotes: 3
Reputation: 2773
The variable is captured because it is a parameter of the function. So it can't be changed from outside the function (except the state of the object). SmtpClient is also a local variable so in the same scope as mail.
Upvotes: 1
Reputation: 157136
Yes, it is still safe. In the context of the method MailMessage mail
doesn't change of meaning there (there is no iteration resetting it over and over again).
That means your code is safe to use and the closure is intact. (You do know you'd better use using
for these cases, right?)
Upvotes: 2