Answers:
其他人已经介绍了Dispose
和之间的区别Finalize
(顺便说一句,该Finalize
方法在语言规范中仍被称为析构函数),因此,我将对该Finalize
方法派上用场的情况做些补充。
某些类型以易于使用和一次性处理的方式封装一次性资源。一般用法通常是这样的:打开,读取或写入,关闭(处置)。它非常适合该using
构造。
其他人则有点困难。WaitEventHandles
实例没有像这样使用,因为它们用于从一个线程向另一个线程发出信号。问题就变成了谁应该调用Dispose
这些?作为此类保护措施类型Finalize
,可以实现一种方法,该方法可确保在应用程序不再引用该实例时分配资源。
Finalize
可能有道理的最大情况是,有许多对象对保持资源有兴趣,但没有办法使对资源不再感兴趣的对象能够发现它是否是资源。最后一个。在这种情况下,Finalize
通常只会在没有人对该对象感兴趣时才会触发。松散的时间Finalize
对于不可替代的资源(例如文件和锁)来说是可怕的,但对于可替代的资源可能没问题。
当对象被垃圾回收并且无法保证何时发生时,将调用finalizer方法(可以强制执行,但会影响性能)。
Dispose
另一方面,该方法应由创建您的类的代码调用,以便您可以在代码完成后立即清理并释放已获取的任何资源(非托管数据,数据库连接,文件句柄等)。你的对象。
标准做法是实施IDisposable
,Dispose
以便您可以在using
陈述中使用对象。如using(var foo = new MyObject()) { }
。在终结器中,您调用Dispose
,以防万一调用代码忘了处置您。
Finalize是backstop方法,由垃圾回收器回收对象时调用。Dispose是一种“确定性清除”方法,应用程序调用该方法以在不再需要它们时释放有价值的本机资源(窗口句柄,数据库连接等),而不是无限期地保留它们,直到GC移至该对象为止。
作为对象的用户,您始终使用Dispose。Finalize用于GC。
作为类的实现者,如果您拥有应该处置的托管资源,则可以实施Dispose。如果您拥有本地资源,那么您将同时实现Dispose和Finalize,并且都调用释放该本地资源的通用方法。这些习惯用法通常通过专用的Dispose(bool dispose)方法进行组合,该方法将Dispose的调用结果为true,而Finalize的调用结果为false。此方法始终释放本机资源,然后检查处理参数,如果为true,则处理托管资源并调用GC.SuppressFinalize。
Dispose
很好,正确实施通常很容易。 Finalize
是邪恶的,正确实施通常很难。除其他外,由于GC将确保只要存在对该对象的任何引用,就不会“回收”任何对象的身份,因此很容易清理一堆Disposable
对象,其中一些对象可能已经被清理了。没问题; 对Dispose
已被调用的对象的任何引用将保留对已被调用的对象的引用Dispose
。
Fred
拥有文件句柄#42并将其关闭,则系统可能会将同一编号附加到提供给其他实体的文件句柄中。在这种情况下,文件句柄#42将不会引用Fred的已关闭文件,而会引用该另一实体正在使用的文件;对于Fred
再次试图靠近手柄#42将是灾难性的。尝试100%可靠地跟踪是否已释放一个非托管对象是可行的。试图跟踪多个对象要困难得多。
完成
protected
,而不为,public
否则private
不能直接从应用程序的代码中调用该base.Finalize
方法,同时,它可以调用该方法处理
IDisposable
对具有终结器的每种类型实施Dispose
方法后,请确保使对象不可用。换句话说,避免Dispose
在调用方法后使用对象。Dispose
所有IDisposable
类型的调用Dispose
多次调用而不会引发错误。Dispose
使用GC.SuppressFinalize
方法禁止以后从方法内部调用终结器Dispose
方法内部抛出异常处置/完成模式
Dispose
以及Finalize
在使用它们时都实现。Finalize
当对象被垃圾回收时,即使开发人员忽略了Dispose
显式调用该方法,该实现也将运行并且资源仍将被释放。Finalize
方法以及Dispose
方法中的非托管资源。另外,从该Dispose
方法中为类中具有组件(具有非托管资源作为其成员)的任何.NET对象调用该Dispose
方法。当不再使用此对象时,GC将调用Finalize。
Dispose只是此类用户可以调用以释放任何资源的普通方法。
如果用户忘记了调用Dispose并且该类已实现Finalize,则GC将确保它被调用。
第MCg认证工具包(考试70-483)第193页有一些关键内容:
destructor≈(几乎等于)base.Finalize()
,将析构函数转换为Finalize方法的替代版本,该方法执行析构函数的代码,然后调用基类的Finalize方法。然后它完全不确定,因为依赖于GC,因此您无法知道何时调用。
如果一个类不包含托管资源,也不包含非托管资源,则它不应实现IDisposable
或具有析构函数。
如果该类仅具有托管资源,则应实现IDisposable
但不应具有析构函数。(执行析构函数时,不能确保托管对象仍然存在,因此Dispose()
无论如何都不能调用它们的方法。)
如果类只有非托管资源,它需要实现IDisposable
并且需要一个析构函数,以防程序不调用Dispose()
。
Dispose()
该方法必须安全运行多次。您可以通过使用变量来跟踪它是否曾经运行来实现。
Dispose()
应该释放托管和非托管资源。
析构函数应仅释放不受管的资源。执行析构函数时,不能确保托管对象仍然存在,因此无论如何都不能调用它们的Dispose方法。这是通过使用规范protected void Dispose(bool disposing)
模式获得的,在规范模式下,仅释放(配置)托管资源disposing == true
。
释放资源后,Dispose()
应调用GC.SuppressFinalize
,以便对象可以跳过完成队列。
具有非托管资源和托管资源的类的实现示例:
using System;
class DisposableClass : IDisposable
{
// A name to keep track of the object.
public string Name = "";
// Free managed and unmanaged resources.
public void Dispose()
{
FreeResources(true);
// We don't need the destructor because
// our resources are already freed.
GC.SuppressFinalize(this);
}
// Destructor to clean up unmanaged resources
// but not managed resources.
~DisposableClass()
{
FreeResources(false);
}
// Keep track if whether resources are already freed.
private bool ResourcesAreFreed = false;
// Free resources.
private void FreeResources(bool freeManagedResources)
{
Console.WriteLine(Name + ": FreeResources");
if (!ResourcesAreFreed)
{
// Dispose of managed resources if appropriate.
if (freeManagedResources)
{
// Dispose of managed resources here.
Console.WriteLine(Name + ": Dispose of managed resources");
}
// Dispose of unmanaged resources here.
Console.WriteLine(Name + ": Dispose of unmanaged resources");
// Remember that we have disposed of resources.
ResourcesAreFreed = true;
}
}
}
99%的时间,您都不必担心。:)但是,如果对象包含对非托管资源的引用(例如,窗口句柄,文件句柄),则需要为托管对象提供一种释放这些资源的方法。Finalize提供了对释放资源的隐式控制。它由垃圾收集器调用。处置是一种对资源释放进行明确控制的方法,可以直接调用。
关于垃圾收集这个主题,还有很多要学习的东西,但这只是一个开始。
终结器用于隐式清理-每当类管理绝对必须清理的资源时,都应使用此终结器,否则会泄漏句柄/内存等。
众所周知,正确实现终结器非常困难,应尽可能避免使用- SafeHandle
该类(在.Net v2.0及更高版本中可用)现在意味着您很少(如果有)不再需要实现终结器。
该IDisposable
接口用于显式清理,并且更常用-您应使用此接口允许用户在使用完对象后显式释放或清理资源。
请注意,如果您有终结器,则还应该实现该IDisposable
接口,以允许用户比对象被垃圾回收时更快地显式释放这些资源。
有关我认为是有关终结器和的最佳和最完整的建议集,请参见DG更新:处置,完成和资源管理IDisposable
。
摘要是-
另外,另一个区别是-在Dispose()实现中,您还应该释放托管资源,而这不应在Finalizer中完成。这是因为该对象引用的托管资源很可能在准备完成之前已被清理。
对于使用非托管资源的类,最佳做法是同时定义Dispose()方法和Finalizer,以便在开发人员忘记显式释放对象的情况下用作备用。两者都可以使用共享方法来清理托管和非托管资源:-
class ClassWithDisposeAndFinalize : IDisposable
{
// Used to determine if Dispose() has already been called, so that the finalizer
// knows if it needs to clean up unmanaged resources.
private bool disposed = false;
public void Dispose()
{
// Call our shared helper method.
// Specifying "true" signifies that the object user triggered the cleanup.
CleanUp(true);
// Now suppress finalization to make sure that the Finalize method
// doesn't attempt to clean up unmanaged resources.
GC.SuppressFinalize(this);
}
private void CleanUp(bool disposing)
{
// Be sure we have not already been disposed!
if (!this.disposed)
{
// If disposing equals true i.e. if disposed explicitly, dispose all
// managed resources.
if (disposing)
{
// Dispose managed resources.
}
// Clean up unmanaged resources here.
}
disposed = true;
}
// the below is called the destructor or Finalizer
~ClassWithDisposeAndFinalize()
{
// Call our shared helper method.
// Specifying "false" signifies that the GC triggered the cleanup.
CleanUp(false);
}
我知道的最好的例子。
public abstract class DisposableType: IDisposable
{
bool disposed = false;
~DisposableType()
{
if (!disposed)
{
disposed = true;
Dispose(false);
}
}
public void Dispose()
{
if (!disposed)
{
disposed = true;
Dispose(true);
GC.SuppressFinalize(this);
}
}
public void Close()
{
Dispose();
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// managed objects
}
// unmanaged objects and resources
}
}
C#中的Finalize和Dispose方法之间的区别。
GC调用finalize方法来回收非托管资源(例如文件操作,Windows api,网络连接,数据库连接),但是调用GC的时间不是固定的。它被GC隐式调用,这意味着我们对其没有低级控制。
处置方法:从代码中调用它时,我们对其具有较低级别的控制。我们可以在感觉不可用时回收非托管资源。我们可以通过实现IDisposal模式来实现。
类实例通常封装对运行时未管理的资源的控制,例如窗口句柄(HWND),数据库连接等。因此,您应该提供显式和隐式方式来释放这些资源。通过在对象上实现受保护的Finalize方法来提供隐式控制(C#和C ++的托管扩展中的析构函数语法)。在不再有对该对象的任何有效引用之后,垃圾收集器会在某个时候调用此方法。在某些情况下,您可能希望为使用对象的程序员提供在垃圾收集器释放对象之前显式释放这些外部资源的能力。如果外部资源稀少或昂贵,如果程序员在不再使用资源时显式释放资源,则可以实现更好的性能。要提供显式控制,请实现IDisposable接口提供的Dispose方法。使用该对象完成操作后,对象的使用者应调用此方法。即使对该对象的其他引用仍然有效,也可以调用Dispose。
请注意,即使通过Dispose提供显式控制,也应使用Finalize方法提供隐式清理。如果程序员未能调用Dispose,Finalize提供了备份,以防止资源永久泄漏。
众所周知,处置和最终确定都用于释放非托管资源。但是区别是最终确定使用两个周期来释放资源,而处置则使用一个周期。
要在第一部分回答,您应该提供一些示例,其中人们对完全相同的类对象使用不同的方法。否则很难(甚至奇怪)回答。
至于第二个问题,最好先阅读 IDisposable接口的正确使用,该接口 声称
这是你的选择!但是选择“处置”。
换句话说:GC仅知道终结器(如果有的话。也称为Microsoft的析构函数)。一个好的代码将尝试同时清除(finalizer和Dispose)。