发布:2021/7/13 16:36:20作者:管理员 来源:本站 浏览次数:885
在了解Finalize和Dispose之前,我们需要了解两个概念,一个是托管资源,一个非委托资源。以下是MSDN上提出的Finalize和Dispose方法的使用指南,如果你的类遵循这个标准的话,你写出的类在.Net平台上就是一个“良民”。
Finalize
下面的规则概括了 Finalize 方法的使用指南。
1.仅在要求终结的对象上实现 Finalize。存在与 Finalize 方法相关的性能开销。
如果需要
Finalize 方法,应考虑实现 IDisposable,以使类的用户可以避免调用 Finalize
方法带来的开销。(juky_huang注:在实现IDisposable的类中,可以通过GC.SuppressFinalize来停止Finalize的运行,这样只要显式的调用了Dispose方法,就能给用户提供更小的开销。如果用户没有显式的调用Dispose方法,也就是没有停止Finalize的运行,这样就可以隐式的实现非托管资源的释放)
2.不要使 Finalize 方法更可见。它应该是 protected,而不是 public。 (juky_huang注:这个很重要,Finalize方法一般是系统调用,用户不去显式的调用它)
[Page]3.对象的 Finalize 方法应该释放对象拥有的任何外部资源。此外,Finalize 方法应该仅释放由对象控制的资源。Finalize 方法不应该引用任何其他对象。
4.不要对不是对象的基类的对象直接调用 Finalize 方法。在 C# 编程语言中,这不是有效的操作。
5.从对象的 Finalize 方法调用 base.Finalize 方法。(juky_huang注:就是派生类调用基类的Finalize方法)
注意 基类的 Finalize 方法由 C# 和 C++ 的托管扩展的析构函数语法自动调用。
Dispose
下面的规则概括了 Dispose 方法的使用指南:
1.在封装明确需要释放的资源的类型上实现处置设计方案。用户可以通过调用公共 Dispose 方法释放外部资源。
2.在通常包含控制资源的派生类型的基类型上实现处置设计方案,即使基类型并不需要。如果基类型有
close 方法,这通常指示需要实现 Dispose。在这类情况下,不要在基类型上实现 Finalize
方法。应该在任何引入需要清理的资源的派生类型中实现 Finalize。
3.使用类型的 Dispose 方法释放类型所拥有的任何可处置资源。
4.对实例调用了 Dispose 后,禁止 Finalize 方法通过调用 GC.SuppressFinalize 方法运行。此规则的例外情况是当必须用 Finalize 完成 Dispose 没有覆盖的工作时,但这种情况很少见。
5.如果基类实现 IDisposable,则调用基类的 Dispose 方法。
6.不要假定 Dispose 将被调用。如果 Dispose 未被调用,也应该使用 Finalize 方法释放类型所拥有的非托管资源。
7.处置了资源之后,在该类型(非 Dispose)上从实例方法引发一个 ObjectDisposedException。该规则不适用于 Dispose 方法,因为在不引发异常的情况下,该方法应该可以被多次调用。
8.通过基类型的层次结构将调用传播到
Dispose。Dispose 方法应释放此对象控制的所有资源和此对象所拥有的任何对象。例如,可以创建一个 类似 TextReader
的对象来控制 Stream 和 Encoding,两者均在用户不知道的情况下由 TextReader 创建。另外,Stream 和
Encoding 都可以获取外部资源。当对 TextReader 调用Dispose 方法时,它应该依次对 Stream 和 Encoding
调用 Dispose,使它们释放它们的外部资源。
9.应考虑在调用了对象的 Dispose 方法后不允许使用对象。重新创建已处置的对象是难以实现的方案。
10.允许 Dispose 方法被调用多次而不引发异常。此方法在首次调用后应该什么也不做。
有了以上的基础后,我们看一段代码,这段代码是Dispose的一个实现,这个代码如果仔细的去考虑的话,非常的有趣,在这里我们又会看到C#中一个非常常用的技术,多态性,如果你看过我在前面写的一篇关于虚拟方法的文章的话,你可以从中理解下面代码的精要之处。
public class BaseResource: IDisposable
{
// Pointer to an external unmanaged resource.[Page]
// 非托管资源
private IntPtr handle;
// Other managed resource this class uses.
// 托管资源
private Component Components;
// Track whether Dispose has been called.
// 是否已经释放资源的标志
private bool disposed = false;
// Constructor for the BaseResource object.
public BaseResource()
{
// Insert appropriate constructor code here.
}
// Implement IDisposable.
// Do not make this method virtual.
// A derived class should not be able to override this method.
// 提供给外部用户显示调用的方法,实际操作是在类的带参数的虚函数Dispose(bool disposing)中实现
public void Dispose()
{
// 表示用户显示调用
Dispose(true);
// Take yourself off the Finalization queue
// to prevent finalization code for this object
// from executing a second time.
// 由于用户是显示调用,所以资源释放不再由GC来完成
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios.
// If disposing equals true, the method has been called directly
// or indirectly by a user\'s code. Managed and unmanaged resources
// can be disposed.
// If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
// Check to see if Dispose has already been called.
// 如果已经释放,不做再次的操作,出现在用户多次调用的情况下
if(!this.disposed)
{
// If disposing equals true, dispose all managed
// and unmanaged resources.
if(disposing)
{
// Dispose managed resources.
// 用户是显示调用的话,我们就要手工的操作托管资源
Components.Dispose();
}
// Release unmanaged resources. If disposing is false,
// only the following code is executed.
CloseHandle(handle);
handle = IntPtr.Zero;
// Note that this is not thread safe.
// Another thread could start disposing the object
// after the managed resources are disposed,
// but before the disposed flag is set to true.
// If thread safety is necessary, it must be[Page]
// implemented by the client.
}
disposed = true;
}
// Use C# destructor syntax for finalization code.
// This destructor will run only if the Dispose method
// does not get called.
// It gives your base class the opportunity to finalize.
// Do not provide destructors in types derived from this class.
// 析构函数
~BaseResource()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
// 表示本次调用是隐式调用,由Finalize方法调用,即托管资源释放由GC来完成
Dispose(false);
}
// Allow your Dispose method to be called multiple times,
// but throw an exception if the object has been disposed.
// Whenever you do something with this class,
// check to see if it has been disposed.
public void DoSomething()
{
if(this.disposed)
{
throw new ObjectDisposedException();
// Design pattern for a derived class.
// Note that this derived class inherently implements the
// IDisposable interface because it is implemented in the base class.
public class MyResourceWrapper: BaseResource
{
// A managed resource that you add in this derived class.
private ManagedResource addedManaged;
// A native unmanaged resource that you add in this derived class.
private NativeResource addedNative;
private bool disposed = false;
// Constructor for this object.
public MyResourceWrapper()
{
// Insert appropriate constructor code here.
}
// 重写Dispose方法,释放派生类自己的资源,并且调用基类的Dispose方法
protected override void Dispose(bool disposing)
{
if(!this.disposed)
{
try
{
if(disposing)
{
// Release the managed resources you added in
// this derived class here.
addedManaged.Dispose();
}
// Release the native unmanaged resources you added
// in this derived class here.
CloseHandle(addedNative);
this.disposed = true;[Page]
}
finally
{
// Call Dispose on your base class.
base.Dispose(disposing);
}
}
}
}
// 在这里,派生类没有实现~MyResourceWrapper和public Dispose方法,应为他们已经继承了基类的这些特性,这也是我说本示例代码精要之处,他使用到了多态性原理,下面我会简单分析
// This derived class does not have a Finalize method
// or a Dispose method without parameters because it inherits
// them from the base class.
本示例中有两个类一个是基类BaseResource,一个是派生类MyResourceWrapper,首先我们必须理解一下几点:
1.类型的
Dispose 方法应该释放它拥有的所有资源。它还应该通过调用其父类型的 Dispose
方法释放其基类型拥有的所有资源。该父类型的Dispose 方法应该释放它拥有的所有资源并同样也调用其父类型的 Dispose
方法,从而在整个基类型层次结构中传播该模式。
2.如果显式的调用了Dispose方法,我们就在Dispose方法中实现托管资源和非托管资源的释放,使用
GC.SuppressFinalize
方法来停止Finalize方法。因为如果用户调用了Dispose方法,那么我们就不必隐式的完成资源的释放,应为Finalizes会大大的减损性能。(Finalize一般只用于用户没有显式的调用Dispose方法,需要我们隐式完成时才使用)
3.要确保始终正确地清理资源,Dispose 方法应该可以被多次调用而不引发任何异常
例如,现在我们有一个派生类实例A,如果我们显示调用A.Dispose()方法,它会去调用基础中的public
Dispose方法这是由于继承的原因,在public Dispose方法中调用的又是Dispose(bool
disposing)方法,由于这个方法已经被重写,所以它实际调用的不是基类中的Dispose(bool
disposing)方法,而是A自己的Dispose(bool
disposing)方法。这是根据运行时类型来定的。所以最终还是实现了,先调用A中的资源释放,然后才调用base.Dispose方法来完成基类的资源释放。
如果用户没有显示调用Dispose方法,那么Finalize方法就会有效,过程和上面是类似的。
从上面可以看出,对于非托管资源的释放,有一个很好的规则,只要我们按照这个规则来做,你写的代码就是.Net中的“良民”。
© Copyright 2014 - 2024 柏港建站平台 ejk5.com. 渝ICP备16000791号-4