我今天正在处理一些使用System.Net.Mail.MailMessage类的代码,如此
public MailMessage CreateMessage(string fromAddress, string recipient)
{
MailMessage message = new MailMessage(fromAddress, recipient);
message.Subject = subject;
message.Body = body;
return message;
}
忽略了这种方法的微不足道的性质,我得到了编译器警告说
对象'消息'未随附 所有异常路径。呼叫 System.IDisposable.Dispose on object 所有引用之前的'message' 超出范围。
这很有意思,因为编译器警告消息在超出范围之前不会被处理掉,但我会假设返回对它的引用意味着虽然消息变量超出范围,但仍然是对底层对象的引用,在这种情况下,我非常怀疑我会想要处理它。
这让我有点困惑,因为警告信息的含义是你不应该返回一次性物品。这真的是这种情况还是只是编译器警告的情况变得疯狂了?
答案 0 :(得分:7)
此警告的含义是,如果方法抛出(例如在Subject
setter中),您可能会留下一个没有任何引用的无关MailMessage
。
你应该通过以下方式防止这种情况发生:
public MailMessage CreateMessage(string fromAddress, string recipient)
{
MailMessage message = new MailMessage(fromAddress, recipient);
try {
message.Subject = subject;
message.Body = body;
return message;
}
catch {
if (message != null) {
message.Dispose();
}
throw;
}
}
编译器没有任何反对返回IDisposable
实例的内容:)
答案 1 :(得分:1)
我见过的一种有时可能有用的模式是创建一个名为DisposeWrapper< T>的对象。它包含构造函数中提供的类型为T的IDisposable对象,它支持两种方法:Keep和Dispose。在不调用Keep的情况下调用Dispose将在包装的IDisposable上调用Dispose;调用Keep将阻止Dispose击中IDisposable。
为方便起见,使用具有DisposeWrapper< T>的通用工厂方法的静态非泛型DisposeWrapper类可能很方便。允许编译器使用方法类型推断来推断包装类的类型。
因此可以做一些像[我习惯于使用vb语法而不是c#的话,所以如果这不是正确的话,请道歉):
{ using(var wrapper = DisposeWrapper.Create(new SomeDisposableThing)) { ... do some stuff with wrapper.Value; return wrapper.Keep(); } }
如果代码到达“return wrapper.Keep();”,那么将返回wrapper.Value而不会丢弃。如果代码在不调用wrapper.Keep的情况下退出Using语句,则将处理wrapper.Value。
当编译器和工具坚持认为必须捕获代码可以在不清理IDisposable的情况下逃脱的所有可能方式时(尤其是在C#中无法安全地使用具有IDisposable字段的字段初始化程序时)时,有时会很烦人但我认为包装IDisposable应该是让编译器开心。
另一种模式是拥有一个包含IDisposable列表的包装器,并使用通用方法在创建IDisposable时“注册”它们(该方法可以将新的IDisposable作为其适当的类型返回)。这可以是一个在构造函数中使用的好模式;通过一些工作,它也可以用于vb.net中的字段初始化器。