在实现 IDisposable 的类上正确使用 Dispose 方法
我今天正在处理一些使用 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 'message' 在所有引用之前 超出范围。
这很有趣,因为编译器警告消息在超出范围之前不会被处理,但我假设返回对它的引用意味着虽然消息变量超出范围,但仍然是对底层对象,在这种情况下,我非常怀疑我是否想要处置它。
这让我有点困惑,因为警告消息的含义是您不应该归还一次性物品。情况确实如此还是这只是编译器警告发疯的情况?
I was working with some code today that used a System.Net.Mail.MailMessage class like so
public MailMessage CreateMessage(string fromAddress, string recipient)
{
MailMessage message = new MailMessage(fromAddress, recipient);
message.Subject = subject;
message.Body = body;
return message;
}
Ignoring the trivial nature of this method, I got a compiler warning saying
object 'message' is not disposed along
all exception paths. Call
System.IDisposable.Dispose on object
'message' before all references to it
are out of scope.
This is interesting because the compiler is warning that the message is not disposed before it goes out of scope, but I would assume that returning a reference to it would mean that whilst the message variable is out of scope, the would still be a reference to the underlying object, in which case I doubt very much that I would want to dispose it.
This has left me a little confused as the implication of the warning message is that you shouldn't return disposable objects. Is this really the case or is this just a case of compiler warning gone mad?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
此警告的含义是,如果该方法抛出异常(例如在
Subject
setter 中),您可能会留下一个没有人引用的未处理的MailMessage
。您应该通过以下方式来防止这种情况发生:
编译器没有任何措施阻止返回 IDisposable 实例:)
The meaning of this warning is that if the method throws (e.g. in the
Subject
setter), you could be left with an undisposedMailMessage
which noone has any reference to.You are supposed to guard against this happening by something like this:
The compiler doesn't have anything against returning
IDisposable
instances :)我见过的一种有时可能有用的模式是创建一个名为 DisposeWrapper的对象。它保存一个 T 类型的 IDisposable 对象,在构造函数中提供,并且支持两种方法:Keep 和 Dispose。调用 Dispose 而不调用 Keep 将会对包装的 IDisposable 调用 Dispose;调用 Keep 将阻止 Dispose 命中 IDisposable。
为了方便起见,拥有一个带有 DisposeWrapper通用工厂方法的静态非泛型 DisposeWrapper 类可能会很方便。允许编译器使用方法类型推断来推断包装类的类型。
因此,人们可以做类似的事情[我习惯了 vb 语法,而不是 c#,所以如果这不太正确,请道歉):
如果代码到达“returnwrapper.Keep();”,那么wrapper.Value将是已退回且未处理。如果代码退出Using语句而不调用wrapper.Keep,则wrapper.Value将被释放。
当编译器和工具坚持要求必须捕获代码在不清理 IDisposable 的情况下逃逸的所有可能方式时,有时会很烦人(特别是当 C# 中无法安全地使用具有 IDisposable 字段的字段初始值设定项时),但我认为包装 IDisposable 应该使编译器快乐的。
另一种模式是使用一个包含 IDisposable 列表的包装器,并使用通用方法在创建 IDisposable 时“注册”它们(该方法可以返回新的 IDisposable 作为其适当的类型)。这可能是在构造函数中使用的一个很好的模式;只需做一点工作,它也可以用于 vb.net 中的字段初始值设定项。
One pattern I've seen which may sometimes be helpful is to create an object called a DisposeWrapper<T> which holds an IDisposable object of type T, supplied in the constructor, and which supports two methods: Keep and Dispose. Calling Dispose without calling Keep will call Dispose on the wrapped IDisposable; calling Keep will prevent Dispose from hitting the IDisposable.
For convenience, it may be handy to have a static non-generic DisposeWrapper class with a generic factory method for DisposeWrapper<T> to allow the compiler to use method type inference to infer the type of the wrapper class.
One could thus do something like [I'm used to vb syntax, not c#, so apologies if this isn't quite right):
If the code reaches the "return wrapper.Keep();", then wrapper.Value will be returned and not disposed. If the code exits the Using statement without calling wrapper.Keep, then wrapper.Value will be disposed.
It's sometimes annoying when compilers and tools insist that one must trap every possible way code could escape without cleaning up an IDisposable (especially when there's no way in C# to safely use field initializers with IDisposable fields) but I think wrapping an IDisposable should make the compiler happy.
An alternative pattern is to have a wrapper which holds a List of IDisposable, and use a generic method to "register" IDisposables as they are created (the method can return the new IDisposable as its appropriate type). This can be a nice pattern to use in a constructor; with a little work it can also be used for field initializers in vb.net.