在C#中使用Finalize / Dispose方法


381

C#2008

我已经为此工作了一段时间,但我仍然对在代码中使用finalize和dispose方法感到困惑。我的问题如下:

  1. 我知道在处理非托管资源时我们只需要一个终结器即可。但是,如果存在调用非托管资源的托管资源,是否仍需要实现终结器?

  2. 但是,如果我开发的类不直接或间接使用任何非托管资源,是否应该实现,IDisposable以允许该类的客户端使用“ using语句”?

    实现IDisposable只是为了使您的类的客户端能够使用using语句是否可行?

    using(myClass objClass = new myClass())
    {
        // Do stuff here
    }
  3. 我在下面开发了这个简单的代码来演示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);
        }
    }

有关源代码的问题:

  1. 在这里,我没有添加终结器,通常,终结器将由GC调用,而终结器将调用Dispose。由于没有终结器,何时可以调用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
  2. 我使用的是WebClient在我的班级NoGateway课。因为WebClient实现IDisposable接口,这是否意味着WebClient间接使用非托管资源?有严格的规则可以遵循吗?我怎么知道一个类使用非托管资源?


1
解决这个资源释放问题实际上需要这种复杂的设计模式吗?
zinking

Answers:


422

推荐的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;仅当您有要处理的实际非托管资源时,才应实现终结器。即使SuppressFinalize调用了CLR,其与可终结对象的处理方式也与不可终结对象不同。

因此,除非必须这样做,否则不应该声明终结器,但是如果类的继承者Dispose直接使用非托管资源,则可以给类的继承者一个钩子,以调用您自己并实现终结器:

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并且朋友不声明,因为他们声明了自己的终结器),则不要实现终结器,因为GC处理终结类的方式不同,即使您以后取消终结器也是如此。还要注意,即使B没有终结器,它仍然会调用SuppressFinalize以正确处理实现终结器的所有子类。

当一个类实现IDisposable接口时,这意味着当您结束使用该类时,应该删除一些非托管资源。实际资源封装在类中;您无需明确删除它们。只需Dispose()将类调用或包装在中即可using(...) {}确保在必要时清除所有不受管理的资源。


26
我同意小屋。请注意,如果您仅在处理托管资源,则不需要终结器(实际上,您不应尝试从终结器内部访问托管对象(“ this”之外的其他对象),因为没有确定的顺序, GC会清理对象,此外,如果您使用的是.Net 2.0或更高版本,则可以(并且应该)使用SafeHandles来包装非托管句柄,因为Safehandles极大地减少了您为托管类编写终结器的全部需求。 com / bclteam / archive / 2005/03/16 / 396900.aspx
JMarsch,

5
我认为最好在终结器中调用MessageBox.Show(“ Error,” + GetType()。Name +“ not处置”),因为应始终处置一次性对象,如果您这样做失败,那就最好尽早得知这一事实。
erikkallen

95
@erikkallen是个玩笑吗?:)
Alex Norcliffe

2
因为在CLR中需要额外的计算工作来跟踪具有活动终结器的类。-实施终结器会导致这种情况发生。调用GC.SuppressFinalize意味着运行时不应调用Finalizer。无论如何,它仍然会进入第二代。如果您不处理托管资源,请不要添加终结器。密封或未密封的类修饰符与这一点无关。
里奇·梅尔顿

3
@Ritch:引用?那不一定是一件坏事。如果您正在实施IDisposable,无论如何它可能会徘徊一段时间。您可以节省CLR的工作量,即必须从Gen0-> Gen1-> Gen2
thecoop

123

实施的官方模式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
  }

}

@Kyle:谢谢!我也喜欢它:-)有一个跟进到它这里
–Jordão

4
尽管我要注意的一件事是它不能阻止被第二次调用。
HuseyinUslu 2011年

5
@HuseyinUslu:这仅仅是模式的本质。您当然可以添加一个disposed标志并进行相应的检查。
Jordão酒店

2
@didibus:添加一个disposed标志很简单,在处理之前检查它,在处理之后设置它。看这里的想法。您还应该在类的任何方法之前检查标志。说得通?复杂吗?
Jordão酒店

1
+1表示“一个更好的解决方案是制定一条规则,始终必须为需要处理的任何非托管资源创建包装器类”。我在VLC的插件中偶然发现了这一点,从那时起我就一直在使用它。节省了很多头痛...
Franz B.

37

请注意,任何IDisposable实现都应遵循以下模式(IMHO)。我根据来自几个出色的.NET“神” .NET Framework设计指南的信息开发了这种模式(请注意,由于某些原因,MSDN不遵循此原则!)。.NET Framework设计指南由Krzysztof Cwalina(当时为CLR架构师)和Brad Abrams(我相信当时为CLR程序经理)和Bill Wagner([有效的C#]和[更有效的C#](在Amazon.com上查找以下内容:

请注意,除非您的类直接包含(而不继承)非托管资源,否则切勿实现Finalizer。一旦在类中实现了终结器,即使从未调用过终结器,也可以保证存在额外的集合。它会自动放置在“完成队列”(在单个线程上运行)上。另外,有一个非常重要的说明...在终结器中执行的所有代码(您需要实现一个)都必须是线程安全的和异常安全的!否则,否则将发生坏的事情(例如,不确定的行为,在异常情况下,致命的不可恢复的应用程序崩溃)。

我整理的模式(并为其编写了代码段)如下:

#region IDisposable implementation

//TODO remember to make this class inherit from IDisposable -> $className$ : IDisposable

// Default initialization for a bool is 'false'
private bool IsDisposed { get; set; }

/// <summary>
/// Implementation of Dispose according to .NET Framework Design Guidelines.
/// </summary>
/// <remarks>Do not make this method virtual.
/// A derived class should not be able to override this method.
/// </remarks>
public void Dispose()
{
    Dispose( true );

    // This object will be cleaned up by the Dispose method.
    // Therefore, you should call GC.SupressFinalize to
    // take this object off the finalization queue 
    // and prevent finalization code for this object
    // from executing a second time.

    // Always use SuppressFinalize() in case a subclass
    // of this type implements a finalizer.
    GC.SuppressFinalize( this );
}

/// <summary>
/// Overloaded Implementation of Dispose.
/// </summary>
/// <param name="isDisposing"></param>
/// <remarks>
/// <para><list type="bulleted">Dispose(bool isDisposing) executes in two distinct scenarios.
/// <item>If <paramref name="isDisposing"/> equals true, the method has been called directly
/// or indirectly by a user's code. Managed and unmanaged resources
/// can be disposed.</item>
/// <item>If <paramref name="isDisposing"/> 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.</item></list></para>
/// </remarks>
protected virtual void Dispose( bool isDisposing )
{
    // TODO If you need thread safety, use a lock around these 
    // operations, as well as in your methods that use the resource.
    try
    {
        if( !this.IsDisposed )
        {
            if( isDisposing )
            {
                // TODO Release all managed resources here

                $end$
            }

            // TODO Release all unmanaged resources here



            // TODO explicitly set root references to null to expressly tell the GarbageCollector
            // that the resources have been disposed of and its ok to release the memory allocated for them.


        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );

        this.IsDisposed = true;
    }
}

//TODO Uncomment this code if this class will contain members which are UNmanaged
// 
///// <summary>Finalizer for $className$</summary>
///// <remarks>This finalizer will run only if the Dispose method does not get called.
///// It gives your base class the opportunity to finalize.
///// DO NOT provide finalizers in types derived from this class.
///// All code executed within a Finalizer MUST be thread-safe!</remarks>
//  ~$className$()
//  {
//     Dispose( false );
//  }
#endregion IDisposable implementation

这是在派生类中实现IDisposable的代码。请注意,您不需要在派生类的定义中显式列出来自IDisposable的继承。

public DerivedClass : BaseClass, IDisposable (remove the IDisposable because it is inherited from BaseClass)


protected override void Dispose( bool isDisposing )
{
    try
    {
        if ( !this.IsDisposed )
        {
            if ( isDisposing )
            {
                // Release all managed resources here

            }
        }
    }
    finally
    {
        // explicitly call the base class Dispose implementation
        base.Dispose( isDisposing );
    }
}

我已将此实现发布在我的博客上,网址为: 如何正确实现处置模式


有人还能为派生类(从该基类派生)添加模式
吗?

3
@akjoshi-我已经更新了上面的模式,以包含派生的一次性类的代码。还要注意,永远不要在派生类中实现终结器...
Dave Black

3
微软似乎喜欢在处置方法的末尾设置“ disposed”标志,但这对我来说似乎是错误的。冗余调用“ Dispose”应该什么都不做;尽管通常不会期望递归调用Dispose,但如果尝试释放在构造或其他操作期间发生的异常而使该对象处于无效状态的对象,则可能发生这种情况。我认为在非虚拟包装函数中使用Interlocked.Exchangeon整数IsDisposed标志会更安全。
2012年

@DaveBlack:如果您的基类不使用非托管资源,但派生类使用非托管资源怎么办?那么,您是否必须在派生类中实现Finalizer?如果是这样,如果您无法访问源,您如何知道基类尚未实现呢?
Didier A.

@DaveBlack“我基于来自几个出色的.NET”神“的信息开发了这种模式”“如果众神之一是Jon Skeet,那么我将按照您的建议进行操作。
伊丽莎白2014年

23

我同意pm100(并且应该在我之前的帖子中明确地说过这一点)。

除非需要,否则永远不要在类中实现IDisposable。具体来说,大约有5次您将需要/应该实现IDisposable:

  1. 您的类明确包含(即不通过继承)任何实现IDisposable的托管资源,一旦不再使用该类,应将其清除。例如,如果您的类包含Stream,DbCommand,DataTable等的实例。

  2. 您的类显式包含任何实现Close()方法的托管资源,例如IDataReader,IDbConnection等。请注意,其中某些类确实通过拥有Dispose()和Close()方法来实现IDisposable。

  3. 您的类显式包含非托管资源-例如,COM对象,指针(是的,您可以在托管C#中使用指针,但是必须在“不安全”块中声明它们,等等。对于非托管资源,还应确保在RCW上调用System.Runtime.InteropServices.Marshal.ReleaseComObject(),尽管从理论上讲,RCW是托管包装,但在幕后仍然存在引用计数。

  4. 如果您的班级使用强引用订阅事件。您需要取消注册/从事件中分离出来。在尝试注销/分离它们之前,请务必先确保它们不为null!

  5. 您的课程包含以上内容的任意组合...

对于使用COM对象并必须使用Marshal.ReleaseComObject()的建议替代方法是使用System.Runtime.InteropServices.SafeHandle类。

BCL(基类库团队)在此处有一篇不错的博客文章,网址为http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx

需要特别注意的一点是,如果您正在使用WCF并清理资源,则应始终避免使用“ using”块。在MSDN上有很多博客文章,还有一些关于为什么这是一个坏主意的文章。我也在这里发布了相关内容- 不要在WCF代理中使用“ using()”


3
我相信有第五种情况:如果您的类使用强引用预订事件,则应实现IDisposable并从Dispose方法中的事件中注销自己。
Didier A.

嗨,迪迪布斯。是的,你是对的。我忘了那个。我已经修改了答案,以作为案例。谢谢。
戴夫·布莱克

有关Dispose模式的MSDN文档增加了另一种情况:“考虑在类上实现基本Dispose模式,这些类本身并不拥有不受管理的资源或可抛弃的对象,但很可能具有子类型。System.IO是一个很好的例子。 .Stream类。虽然它是一个不包含任何资源的抽象基类,但其大多数子类都具有,因此,它实现了这种模式。”
Gonen I 2014年

12

使用lambda代替IDisposable。

我从未对整个使用/ IDisposable想法感到兴奋。问题在于它要求调用者执行以下操作:

  • 知道他们必须使用IDisposable
  • 记得使用“使用”。

我的新首选方法是使用工厂方法和lambda代替

想象一下我想用SqlConnection做一些事情(应该包装在using中)。经典地你会做

using (Var conn = Factory.MakeConnection())
{
     conn.Query(....);
}

新方法

Factory.DoWithConnection((conn)=>
{
    conn.Query(...);
}

在第一种情况下,调用者根本无法使用using语法。在第二种情况下,用户别无选择。没有创建SqlConnection对象的方法,调用者必须调用DoWithConnection。

DoWithConnection看起来像这样

void DoWithConnection(Action<SqlConnection> action)
{
   using (var conn = MakeConnection())
   {
       action(conn);
   }
}

MakeConnection 现在是私人的


2
在lambda中包装东西可能是一个好方法,但是它有局限性。实际上,对于一个类的所有使用者都将使用“ using”块的情况,这并不算太糟糕,但是,对于在类字段中直接存储IDisposable方法(直接或在类似迭代器的情况下)的情况,它将是不允许的。 )。
supercat

@supercat,您可以辩称,禁止存储资源消耗大的东西是一件好事。我在这里提出的借贷模型迫使您
精打细算

这可能是一件好事,但也会使某些非常合理的操作变得非常困难。例如,假设数据库读取器类型而不是实现IEnumerable <T>公开了一个方法DoForAll(Action<T>) where T:IComparable<T>,在每个记录上调用指示的委托。给定两个这样的对象,这两个对象都将按排序的顺序返回数据,一个对象如何输出一个集合中存在的所有项目,而另一个对象中没有?如果实现了这些类型IEnumerable<T>,则可以执行合并操作,但不适用于DoForAll
supercat

我可以确定合并两个DoForAll集合而不必首先将一个集合完整地复制到其他结构中的唯一方法是使用两个线程,这将比仅仅使用几个IEnumerable占用更多资源,并且要小心释放它们。
supercat

-1:对未提出的问题的好的答案。这将是“如何简化IDisposable对象的消费”的好答案
John Saunders 2012年

10

没有人回答即使您不需要IDisposable也应该实现IDisposable的问题。

简短答案:否

长答案:

这将允许您的类的使用者使用“使用”。我要问的问题是-他们为什么要这样做?除非他们知道自己必须知道以及如何知道,否则大多数开发人员都不会使用“使用”。要么

  • 它避免了他们的经验(例如套接字类)
  • 其记录
  • 他们非常谨慎,可以看到该类实现了IDisposable

因此,通过实现IDisposable,您可以告诉开发人员(至少一些)此类包装了必须释放的内容。他们将使用“使用”-但在其他情况下,则不可能使用(对象的范围不是局部的)。在其他情况下,他们将不得不开始担心对象的寿命-我肯定会担心。但这不是必需的

您实现了Idisposable以使他们能够使用using,但是除非您告知他们使用,否则他们将不会使用using。

所以不要做


1
我不明白为什么开发人员不会在实现IDisposable的对象上不使用/处置(除非程序将要退出)。
adrianm 2010年

1
关键是开发人员必须编写所有调用以将其放置在所有导致未引用它的代码路径中。因此,例如,如果我将一个实例放入字典中,则当我从字典中删除条目时,我必须调用dispose。在这种情况下,它不需要很多麻烦-不需要处理该对象
pm100,2010年

3
@ PM100回复:毋实现IDisposable的-有一个在详细的文章codeproject.com/KB/dotnet/idisposable.aspx其中讨论了一些罕见的情况下,您可能要考虑这个(非常罕见的,我敢肯定)。简而言之:如果您可以预见将来需要IDisposable或在派生对象中使用IDisposable,则可以考虑在基类中将IDisposable实现为“ no-op”,以避免某些派生对象需要“切片”问题处置而其他人则不这样做。
凯文·莱斯

4
  1. 如果您正在使用其他使用非托管资源的托管对象,则不必负责确保这些对象已完成。您的责任是在对象上调用Dispose时在这些对象上调用Dispose,并且该对象在那里停止。

  2. 如果您的班级不使用任何稀缺资源,那么我看不到您为什么要让您的班级实现IDisposable。仅在以下情况下才应这样做:

    • 知道您很快就会在对象中拥有稀缺资源(只是现在(我的意思是,正如“我们仍在开发中,它将在完成之前就在这里”),而不是“我认为我们将需要此资源” ”)
    • 使用稀缺资源
  3. 是的,使用您的代码的代码必须调用对象的Dispose方法。是的,使用对象的代码可以using按照您的显示使用。

  4. (是否再次2?)WebClient可能使用非托管资源,或者使用其他实现IDisposable的托管资源。但是,确切原因并不重要。重要的是它实现了IDisposable,因此,即使事实证明WebClient根本不使用其他资源,也要靠处理对象时处理该对象来依靠该知识。


4

配置方式:

public abstract class DisposableObject : IDisposable
{
    public bool Disposed { get; private set;}      

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~DisposableObject()
    {
        Dispose(false);
    }

    private void Dispose(bool disposing)
    {
        if (!Disposed)
        {
            if (disposing)
            {
                DisposeManagedResources();
            }

            DisposeUnmanagedResources();
            Disposed = true;
        }
    }

    protected virtual void DisposeManagedResources() { }
    protected virtual void DisposeUnmanagedResources() { }
}

继承示例:

public class A : DisposableObject
{
    public Component components_a { get; set; }
    private IntPtr handle_a;

    protected override void DisposeManagedResources()
    {
        try
        {
          Console.WriteLine("A_DisposeManagedResources");
          components_a.Dispose();
          components_a = null;
        }
        finally
        { 
          base.DisposeManagedResources();
        }
    }

    protected override void DisposeUnmanagedResources()
    {
        try
        {
          Console.WriteLine("A_DisposeUnmanagedResources");
          CloseHandle(handle_a);
          handle_a = IntPtr.Zero;
        }
        finally
        { 
          base.DisposeUnmanagedResources();
        }
    }
}

public class B : A
{
    public Component components_b { get; set; }
    private IntPtr handle_b;

    protected override void DisposeManagedResources()
    {
        try
        {
          Console.WriteLine("B_DisposeManagedResources");
          components_b.Dispose();
          components_b = null;
        }
        finally
        { 
          base.DisposeManagedResources();
        }
    }

    protected override void DisposeUnmanagedResources()
    {
        try
        {
          Console.WriteLine("B_DisposeUnmanagedResources");
          CloseHandle(handle_b);
          handle_b = IntPtr.Zero;
        }
        finally
        { 
          base.DisposeUnmanagedResources();
        }
    }
}

4

另一个答案的某些方面由于两个原因而有些不正确:

第一,

using(NoGateway objNoGateway = new NoGateway())

实际上等于:

try
{
    NoGateway = new NoGateway();
}
finally
{
    if(NoGateway != null)
    {
        NoGateway.Dispose();
    }
}

这听起来很荒谬,因为除非您有OutOfMemory异常,否则“ new”运算符不应返回“ null”。但是请考虑以下情况:1.调用一个返回IDisposable资源的FactoryClass或2.如果您的类型可能会或可能不会从IDisposable继承,具体取决于其实现-请记住,我已经看到IDisposable模式实现不正确在许多客户端,开发人员只是添加Dispose()方法而不继承自IDisposable(不好,不好,不好)。您还可能会遇到从属性或方法返回IDisposable资源的情况(同样很糟糕,不好,不好-不要“放弃您的IDisposable资源”)

using(IDisposable objNoGateway = new NoGateway() as IDisposable)
{
    if (NoGateway != null)
    {
        ...

如果'as'运算符返回null(或返回资源的属性或方法),并且'using'块中的代码可以防止'null',则由于以下原因,在尝试对null对象调用Dispose时,代码不会爆炸: “内置”空检查。

您的回复不准确的第二个原因是由于以下stmt:

GC销毁对象时,将调用终结器

首先,最终确定(以及GC本身)是不确定的。CLR确定何时调用终结器。即开发人员/代码不知道。如果正确实现了IDisposable模式(如我在上面所述),并且已调用GC.SuppressFinalize(),则不会调用Finalizer。这是正确正确实施模式的重要原因之一。由于每个托管进程只有1个Finalizer线程,无论逻辑处理器的数量如何,您都可以通过备份甚至挂起Finalizer线程而忘记调用GC.SuppressFinalize(),从而轻易降低性能。

我已经在我的博客上发布了Dispose Pattern的正确实现:如何正确实现Dispose Pattern


2
你确定写作NoGateway = new NoGateway();NoGateway != null
心教堂

1
这是指stackoverflow.com/a/898856/3195477吗?现在没有名称“ Icey”发布的答案
UuDdLrLrSs

@DaveInCaz看起来是正确的。我在任何地方都看不到“冰冷的”,但我的回答上下文似乎针对您上面的链接提供的答案。也许他更改了用户名?
戴夫·布莱克

@DaveBlack很酷,谢谢。我刚刚将其编辑成文本。
UuDdLrLrSs

2

1)WebClient是托管类型,因此您不需要终结器。如果您的用户不使用NoGateway类的Dispose(),并且此后需要清除本机类型(GC不会收集),则需要使用终结器。在这种情况下,如果用户不调用Dispose(),则在NoGateway之后,GC将立即处置所包含的WebClient。

2)间接是,但是您不必为此担心。您的代码是正确的,并且您不能阻止用户非常容易地忘记使用Dispose()。


2

来自msdn的模式

public class BaseResource: IDisposable
{
   private IntPtr handle;
   private Component Components;
   private bool disposed = false;
   public BaseResource()
   {
   }
   public void Dispose()
   {
      Dispose(true);      
      GC.SuppressFinalize(this);
   }
   protected virtual void Dispose(bool disposing)
   {
      if(!this.disposed)
      {        
         if(disposing)
         {
            Components.Dispose();
         }         
         CloseHandle(handle);
         handle = IntPtr.Zero;
       }
      disposed = true;         
   }
   ~BaseResource()      
   {      Dispose(false);
   }
   public void DoSomething()
   {
      if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}
public class MyResourceWrapper: BaseResource
{
   private ManagedResource addedManaged;
   private NativeResource addedNative;
   private bool disposed = false;
   public MyResourceWrapper()
   {
   }
   protected override void Dispose(bool disposing)
   {
      if(!this.disposed)
      {
         try
         {
            if(disposing)
            {             
               addedManaged.Dispose();         
            }
            CloseHandle(addedNative);
            this.disposed = true;
         }
         finally
         {
            base.Dispose(disposing);
         }
      }
   }
}

1
using(NoGateway objNoGateway = new NoGateway())

相当于

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

GC销毁对象时,将调用终结器。这可能与您离开方法的时间完全不同。离开using块后,将立即调用IDisposable的Dispose。因此,该模式通常用于在不再需要资源后立即使用释放资源。


1
GC销毁对象时,不会调用终结器。如果“最终化”被覆盖,则当GC 否则会销毁该对象时,它将被放置在需要最终确定的对象队列中,从而临时创建对该对象的强大引用,并且至少是暂时“恢复”它。
supercat

-5

据我所知,强烈建议不要使用终结器/析构函数:

public ~MyClass() {
  //dont use this
}

通常,这是由于不知道何时或如果调用它。处置方法要好得多,尤其是如果您使用或直接处置。

使用是好的。用它 :)


2
您应该点击thecoop答案中的链接。是的,使用/ Dispose更好,但是Disposable类应该同时实现这两个。
汉克·霍尔特曼

有趣的是,我从Microsoft读取的所有文档-例如框架设计指南-都说不要使用析构函数。始终使用IDisposable。
尼克·怀斯

5
只需区分使用类和编写类,然后再次阅读即可。
汉克·霍尔特曼

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.