如果您从一个拥有 IDispose 接口的类派生出一个类,您需要实现 Dispose 方法吗?
如果您从一个拥有 IDispose 接口的类派生出一个类,您需要实现 Dispose 方法吗?
C# 2008
我已经在这个问题上工作了一段时间了,但对代码中 finalize 和 dispose 方法的使用仍然感到困惑。我的问题如下:
- 我知道只有在处理非托管资源时才需要使用终结器(finalizer)。但是,如果有托管资源调用非托管资源,是否仍然需要实现终结器(finalizer)?
- 如果我开发的类不使用任何非托管资源——直接或间接——那么是否应该实现
IDisposable
接口以允许该类的客户端使用“using 语句”?是否可行只是为了启用你的类的客户端使用 using 语句而实现 IDisposable 接口?
using(myClass objClass = new myClass()) { // Do stuff here }
- 我编写了下面的简单代码来演示 Finalize/dispose 的使用:
public class NoGateway : IDisposable { private WebClient wc = null; public NoGateway() { wc = new WebClient(); wc.DownloadStringCompleted += wc_DownloadStringCompleted; } // Start the Async call to find if NoGateway is true or false public void NoGatewayStatus() { // Start the Async's download // Do other work here wc.DownloadStringAsync(new Uri(www.xxxx.xxx)); } private void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { // Do work here } // Dispose of the NoGateway object public void Dispose() { wc.DownloadStringCompleted -= wc_DownloadStringCompleted; wc.Dispose(); GC.SuppressFinalize(this); } }
对源代码的问题:
- 这里我没有添加终结器(finalizer),正常情况下终结器(finalizer)会被 GC 调用,并且终结器(finalizer)会调用 Dispose 方法。由于我没有终结器(finalizer),那么 Dispose 方法在何时调用呢?是类的客户端必须调用它吗?
所以我的例子中的类被称为 NoGateway,客户端可以像这样使用和释放该类:
using(NoGateway objNoGateway = new NoGateway()) { // Do stuff here }
当执行到 using 块的结尾时,Dispose 方法是否会被自动调用,还是客户端必须手动调用 Dispose 方法?即
NoGateway objNoGateway = new NoGateway(); // Do stuff with object objNoGateway.Dispose(); // finished with it
- 我在我的 NoGateway 类中使用了 WebClient 类。因为 WebClient 实现了 IDisposable 接口,这是否意味着 WebClient 间接地使用了非托管资源?是否有一条硬性的规则来遵循这一点?我如何知道一个类是否使用了非托管资源?
官方的实现 IDisposable
模式很难理解。我认为这个 更好:
public class BetterDisposableClass : IDisposable { public void Dispose() { CleanUpManagedResources(); CleanUpNativeResources(); GC.SuppressFinalize(this); } protected virtual void CleanUpManagedResources() { // ... } protected virtual void CleanUpNativeResources() { // ... } ~BetterDisposableClass() { CleanUpNativeResources(); } }
一个 更好的 解决方案是规定你必须为任何需要处理的非托管资源创建一个封装器类:
public class NativeDisposable : IDisposable { public void Dispose() { CleanUpNativeResource(); GC.SuppressFinalize(this); } protected virtual void CleanUpNativeResource() { // ... } ~NativeDisposable() { CleanUpNativeResource(); } }
使用 SafeHandle
及其派生类,这些类应该非常罕见。
对于不直接处理非托管资源的可释放类,即使存在继承,在强大的: 它们不再需要担心非托管资源。它们将易于实现和理解:
public class ManagedDisposable : IDisposable { public virtual void Dispose() { // dispose of managed resources } }
推荐使用IDisposable模式,这里有详细介绍。编写一个使用IDisposable的类时,通常应使用两种模式:
实现一个不使用非托管资源的密封类时,只需像实现常规接口一样实现Dispose方法:
public sealed class A : IDisposable { public void Dispose() { // get rid of managed resources, call Dispose on member variables... } }
实现非密封类时,应像这样实现:
public class B : IDisposable { public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { // get rid of managed resources } // get rid of unmanaged resources } // only if you use unmanaged resources directly in B //~B() //{ // Dispose(false); //} }
请注意,我没有在B
中声明finalizer;仅在存在实际非托管资源需要处理的情况下,才应实现finalizer。即使调用SuppressFinalize,CLR也会对需要finalizer的对象和不需要finalizer的对象进行不同的处理。
因此,除非必须,否则不应声明finalizer,但您可以为您的类的继承者提供一个钩子来调用您的Dispose并在需要时实现finalizer:
public class C : B { private IntPtr m_Handle; protected override void Dispose(bool disposing) { if (disposing) { // get rid of managed resources } ReleaseHandle(m_Handle); base.Dispose(disposing); } ~C() { Dispose(false); } }
如果没有直接使用非托管资源(SafeHandle
和朋友们不算,因为它们声明自己的finalizer),则不要实现finalizer,因为即使稍后压制finalizer,GC也会以不同方式处理可终结类。还要注意,即使B
没有finalizer,它仍将调用SuppressFinalize以正确处理任何实现finalizer的子类。
当一个类实现IDisposable接口时,意味着在您使用完该类时应该处理一些非托管资源。实际资源封装在类中,不需要显式删除它们。仅调用Dispose()
或将类包装在using(...) {}
中将确保必要时处理任何非托管资源。