如何做 C++ C# 中的样式析构函数?
我通过 IDisposable
获得了一个带有 Dispose
函数的 C# 类。 它旨在在 using
块内使用,以便可以立即释放它处理的昂贵资源。
问题在于,在调用 Dispose
之前抛出异常,并且程序员忽略使用 using
或 finally
时,就会出现错误。
在 C++ 中,我从来不用担心这个。 对类的析构函数的调用将自动插入到对象作用域的末尾。 避免这种情况发生的唯一方法是使用 new 运算符并将对象保存在指针后面,但这对程序员来说需要额外的工作,这并不是他们偶然会做的事情,比如忘记使用 using.
有什么方法可以在 C# 中自动使用 using
块吗?
非常感谢。
更新:
我想解释一下为什么我不接受终结器的答案。 这些答案本身在技术上是正确的,但它们不是 C++ 风格的析构函数。
这是我发现的错误,简化为要点...
try
{
PleaseDisposeMe a = new PleaseDisposeMe();
throw new Exception();
a.Dispose();
}
catch (Exception ex)
{
Log(ex);
}
// This next call will throw a time-out exception unless the GC
// runs a.Dispose in time.
PleaseDisposeMe b = new PleaseDisposeMe();
使用 FXCop 是一个很好的建议,但如果这是我唯一的答案,我的问题将不得不成为对 C# 人员的恳求,或者使用 C++ 。 有人知道二十个嵌套的 using 语句吗?
I've got a C# class with a Dispose
function via IDisposable
. It's intended to be used inside a using
block so the expensive resource it handles can be released right away.
The problem is that a bug occurred when an exception was thrown before Dispose
was called, and the programmer neglected to use using
or finally
.
In C++, I never had to worry about this. The call to a class's destructor would be automatically inserted at the end of the object's scope. The only way to avoid that happening would be to use the new operator and hold the object behind a pointer, but that required extra work for the programmer isn't something they would do by accident, like forgetting to use using
.
Is there any way to for a using
block to be automatically used in C#?
Many thanks.
UPDATE:
I'd like to explain why I'm not accepting the finalizer answers. Those answers are technically correct in themselves, but they are not C++ style destructors.
Here's the bug I found, reduced to the essentials...
try
{
PleaseDisposeMe a = new PleaseDisposeMe();
throw new Exception();
a.Dispose();
}
catch (Exception ex)
{
Log(ex);
}
// This next call will throw a time-out exception unless the GC
// runs a.Dispose in time.
PleaseDisposeMe b = new PleaseDisposeMe();
Using FXCop
is an excellent suggestion, but if that's my only answer, my question would have to become a plea to the C# people, or use C++. Twenty nested using statements anyone?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
最佳实践是在类中使用终结器并始终使用
using
块。虽然没有真正的直接等价物,终结器看起来像 C 析构函数,但行为不同。
您应该嵌套
using
块,这就是为什么 C# 代码布局默认将它们放在同一行...当您不使用
using
时,您可以无论如何,做它在幕后所做的事情:使用托管代码,C# 非常非常擅长管理自己的内存,即使东西处理不当也是如此。 如果您经常处理非托管资源,那么它就不那么强大了。
The best practice is to use a finaliser in your class and always use
using
blocks.There isn't really a direct equivalent though, finalisers look like C destructors, but behave differently.
You're supposed to nest
using
blocks, that's why the C# code layout defaults to putting them on the same line...When you're not using
using
you can just do what it does under the hood anyway:With managed code C# is very very good at looking after its own memory, even when stuff is poorly disposed. If you're dealing with unmanaged resources a lot it's not so strong.
这与程序员忘记在 C++ 中使用删除没有什么不同,只不过至少在这里垃圾收集器最终仍会赶上它。
如果您唯一担心的资源是内存,那么您永远不需要使用 IDisposable。 该框架将自行处理该问题。 IDisposable 仅适用于非托管资源,例如数据库连接、文件流、套接字等。
This is no different from a programmer forgetting to use delete in C++, except that at least here the garbage collector will still eventually catch up with it.
And you never need to use IDisposable if the only resource you're worried about is memory. The framework will handle that on it's own. IDisposable is only for unmanaged resources like database connections, filestreams, sockets, and the like.
更好的设计是让此类在处置之前自行释放昂贵的资源。
例如,如果它是数据库连接,则仅在需要时连接并立即释放,远远早于实际的类被释放。
A better design is to make this class release the expensive resource on its own, before its disposed.
For example, If its a database connection, only connect when needed and release immediately, long before the actual class gets disposed.
编辑(粗体):
当对象移出范围并由垃圾收集器整理时,If 将被调用但是这不是确定性的,并且不能保证在任何特定时间发生。
这称为终结器。 所有具有终结器的对象都会被垃圾收集器放入一个特殊的终结队列,并在其中调用它们的终结方法(因此从技术上讲,声明空终结器会影响性能)。
根据框架指南,对于非托管资源,“接受的”处置模式如下:
因此,上面的意思是,如果有人调用 Dispose,非托管资源就会被整理。 然而,如果有人忘记调用 Dispose 或发生阻止调用 Dispose 的异常,非托管资源仍然会被清理掉,只是稍后 GC 开始清理(包括应用程序关闭或意外结束) )。
EDIT (bold):
If will get called when the object is moved out of scope and is tidied by the garbage collector however this is not deterministic and is not guaranteed to happen at any particular time.
This is called a Finalizer. All objects with a finaliser get put on a special finalise queue by the garbage collector where the finalise method is invoked on them (so it's technically a performance hit to declare empty finalisers).
The "accepted" dispose pattern as per the Framework Guidelines is as follows with unmanaged resources:
So the above means that if someone calls Dispose the unmanaged resources are tidied. However in the case of someone forgetting to call Dispose or an exception preventing Dispose from being called the unmanaged resources will still be tidied away, only slightly later on when the GC gets its grubby mitts on it (which includes the application closing down or unexpectedly ending).
在我工作的地方,我们使用以下准则:
为了让我们的生活更轻松,我们的基础设施中还有一个 SafeDispose 方法,它在 try-catch 块中调用其参数的 Dispose 方法(带有错误日志记录),以防万一(尽管 Dispose 方法不应该抛出异常)。
的建议
另请参阅:Chris Lyon 关于 IDisposable Edit :
@Quarrelsome:你应该做的一件事是在“Dispose”中调用 GC.SuppressFinalize,这样如果对象被处置,它就不会被“重新处置”。
通常还建议保留一个标志来指示该对象是否已被处置。 以下模式通常非常好:
当然,锁定并不总是必要的,但如果您不确定您的类是否会在多线程环境中使用,建议保留它。
Where I work we use the following guidelines:
To make our lives easier, we also have a SafeDispose method in our infrastructure, which calls the the Dispose method of its argument within a try-catch block (with error logging), just in case (although Dispose methods are not supposed to throw exceptions).
See also: Chris Lyon's suggestions regarding IDisposable
Edit:
@Quarrelsome: One thing you ought to do is call GC.SuppressFinalize inside 'Dispose', so that if the object was disposed, it wouldn't be "re-disposed".
It is also usually advisable to hold a flag indicating whether the object has already been disposed or not. The follwoing pattern is usually pretty good:
Of course, locking is not always necessary, but if you're not sure if your class would be used in a multi-threaded environment or not, it is advisable to keep it.
不幸的是,没有任何方法可以直接在代码中执行此操作。 如果这是内部问题,有各种代码分析解决方案可以捕获此类问题。 你调查过FxCop吗? 我认为这将捕获这些情况以及 IDisposable 对象可能挂起的所有情况。 如果它是人们在组织外部使用的组件,并且您不需要 FxCop,那么文档实际上是您唯一的资源:)。
编辑:对于终结器来说,这并不能真正保证终结何时发生。 所以这可能是您的解决方案,但这取决于具体情况。
Unfortunately there isn't any way to do this directly in the code. If this is an issue in house, there are various code analysis solutions that could catch these sort of problems. Have you looked into FxCop? I think that this will catch these situations and in all cases where IDisposable objects might be left hanging. If it is a component that people are using outside of your organization and you can't require FxCop, then documentation is really your only recourse :).
Edit: In the case of finalizers, this doesn't really guarantee when the finalization will happen. So this may be a solution for you but it depends on the situation.
@吵架
这个声明具有误导性,我的理解也不正确:绝对不能保证何时调用终结器。 你说 billpg 应该实现终结器是完全正确的; 然而,当对象超出他想要的范围时,它不会被自动调用。 证据,第一个要点 Finalize 操作有以下限制。
事实上,Microsoft 向 Chris Sells 提供了一笔资助,以创建使用引用计数而不是垃圾收集的 .NET 实现 链接。 事实证明,性能受到相当大的影响。
@Quarrelsome
This statement is misleading and how I read it incorrect: There is absolutely no guarantee when the finalizer will be called. You are absolutely correct that billpg should implement a finalizer; however it will not be called automaticly when the object goes out of scope like he wants. Evidence, the first bullet point under Finalize operations have the following limitations.
In fact Microsoft gave a grant to Chris Sells to create an implementation of .NET that used reference counting instead of garbage collection Link. As it turned out there was a considerable performance hit.