我忙于对非确定性破坏感到困惑。在对另一个question的回答中,我得到了析构函数/终结符(我假设在c#中是相同的东西,即名为~classname()的函数)的建议是昂贵的而不是必需的。但是看this示例,使用了析构函数,并且从注释中听起来可能是至关重要的。任何人都有一些关于这一切是如何组合在一起的建议,我应该从我的代码中删除析构函数吗?
再次感谢。
答案 0 :(得分:3)
如果您绝对 在某个时刻运行某些清理,那么您应该只包含一个终结器,无论它是否明确执行。在这种情况下,您应该始终有一种明确的方式及时执行清理,并且无论如何都应该取消最终确定,以便“好”客户看不到任何性能损失。
如果您对非托管资源有直接处理,通常只需要一个终结器 - 如果您只引用另一个具有资源句柄的类(例如FileStream
)那么你应该把它留给另一个班级来完成一个终结者。
随着.NET 2.0中SafeHandle
的出现,值得编写自己的终结器的情况是very rare indeed。
终结器的性能损失是它们使你的对象的生存时间超过了他们所需的时间:在第一个GC周期中,它们被认为有资格收集,它们会被放到终结器队列中 - 然后碰到下一代就像在GC循环中幸存的任何其他对象一样。然后终结器将在另一个线程中运行(在某些时候),只有然后它们才有资格真正被收集。因此,不是(比如)收集第一代gen1集合,而是直到下一个 gen2 集合,这可能会相当晚。
答案 1 :(得分:1)
通常,实现析构函数在以下情况下很有用:当不能保证时,该客户端代码将正确关闭所有资源(文件流,数据库连接等)。因此,如果客户端代码无法执行此操作,您将拥有代码,这将关闭它,这比仅保留资源打开更好。
答案 2 :(得分:1)
在直接处理非托管资源时,您只需要完整的Disposable模式。然后由您的调用代码来确保析构函数(几乎)从未使用过。
在处理托管资源(=非托管资源的间接所有权)时,析构函数是无用的:
class FileWrapper
{
private FileStream fs; // managed resource
~FileWrapper()
{
if (fs != null)
fs.Dispose(); // fs is already on the GC finalizer queue
}
}
每当GC收集FileWrapper对象时,都可以确定fs对象属于同一批次。因此,对fs.Dispose()的调用是无用的,只测试FileStream.Dispose()的正确(允许多次调用)行为。
这里唯一有用的析构函数是FileStream中的析构函数。