在 C 语言中使用 Finalize/Dispose 方法#

Use of Finalize/Dispose method in C#

提问人:ant2009 提问时间:5/23/2009 最后编辑:Niels R.ant2009 更新时间:11/25/2019 访问量:204870

问:

C# 2008 (英语)

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

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

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

    实现 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. 我在课堂上使用该类。因为实现了接口,这是否意味着间接使用非托管资源?有没有硬性规定可以遵循?我如何知道一个类使用了非托管资源?WebClientNoGatewayWebClientIDisposableWebClient

C# .NET IDoposable 终结器

评论


答:

1赞 Daniel Fabian 5/23/2009 #1
using(NoGateway objNoGateway = new NoGateway())

相当于

try
{
    NoGateway = new NoGateway();
}

finally
{
    NoGateway.Dispose();
}

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

评论

1赞 supercat 12/14/2011
当 GC 销毁对象时,不会调用终结器。如果“Finalize”被覆盖,那么当 GC 本来会销毁该对象时,它将被放置在需要完成该对象的队列中,暂时创建对它的强引用并 - 至少暂时 - “复活”它。
2赞 Jesse C. Slicer 5/23/2009 #2

1) WebClient 是托管类型,因此不需要终结器。如果你的用户没有 Dispose() 你的 NoGateway 类,并且本机类型(GC 不收集)之后需要清理,则需要终结器。在这种情况下,如果用户不调用 Dispose(),则 GC 将在 NoGateway 调用后立即释放包含的 WebClient。

2)间接是的,但你不必担心。您的代码是正确的,您无法阻止用户轻易忘记 Dispose()。

455赞 thecoop 5/23/2009 #3

建议的 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);
    //}
}

请注意,我没有在 ;仅当有实际的非托管资源要处置时,才应实现终结器。CLR 处理可终结对象的方式与处理不可终结对象的方式不同,即使调用了 CLR 也是如此。BSuppressFinalize

因此,除非必要,否则不应声明终结器,但是如果类的继承者直接使用非托管资源,则可以为其提供一个钩子来调用 uer 并自己实现终结器: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);
    }
}

如果你没有直接使用非托管资源(朋友不算在内,因为他们声明了自己的终结器),那么就不要实现终结器,因为 GC 以不同的方式处理可终结的类,即使你后来禁止了终结器。另请注意,即使没有终结器,它仍然会调用正确处理任何实现终结器的子类。SafeHandleBSuppressFinalize

当类实现 IDisposable 接口时,这意味着在使用完该类后,应删除某些非托管资源。实际资源封装在类中;您无需显式删除它们。只需调用或将类包装在 a 中,即可确保在必要时删除任何非托管资源。Dispose()using(...) {}

评论

29赞 JMarsch 5/23/2009
我同意thecoop。请注意,如果您只处理托管资源,则不需要终结器(事实上,您不应该尝试从终结器中访问托管对象(“this”除外),因为 GC 清理对象的顺序没有保证。此外,如果您使用的是 .Net 2.0 或更高版本,则可以(并且应该)使用 SafeHandles 来包装非托管句柄。Safehandles 大大减少了您为托管类编写终结器的需要。blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx
2赞 Ritch Melton 3/9/2011
因为 CLR 中需要额外的计算工作来跟踪具有活动终结器的类。- 实现终结器会导致这种情况发生。调用 GC.SuppressFinalize 表示运行时不应调用 Finalizer。无论如何,它仍然是 Gen2。如果不处理托管资源,请不要添加终结器。密封或未密封的类修饰符与这一点无关。
3赞 thecoop 3/9/2011
@Ritch:引文?这不一定是坏事;如果你正在实现,它很可能会徘徊一段时间。您节省了 CLR 从 Gen0 -> Gen1 -> Gen2 复制它的工作量IDisposable
1赞 Ritch Melton 3/9/2011
@thecoop:我不同意。我使用的一次性用品本质上是事务性的,持续时间不会超过一次功能调用。
2赞 supercat 12/14/2011
@thecoop:最终效果是,每个具有未抑制终结器的对象都至少存活了一个集合的时间,并且每个可终结对象具有直接或间接引用的对象的持续时间至少与该可终结对象本身一样长。
4赞 Lasse V. Karlsen 5/23/2009 #4
  1. 如果您使用使用非托管资源的其他托管对象,则您没有责任确保这些对象最终确定。你的职责是在对你的对象调用 Dispose 时对这些对象调用 Dispose,并就此停止。

  2. 如果你的类不使用任何稀缺资源,我不明白你为什么要让你的类实现 IDisposable。只有在符合以下条件时,才应这样做:

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

  4. (又是 2 个?WebClient 可能使用非托管资源或实现 IDisposable 的其他托管资源。然而,确切的原因并不重要。重要的是它实现了 IDisposable,因此,当你完成该对象的处理后,它就落在了你对该知识的处理上,即使事实证明 WebClient 根本不使用其他资源。

-5赞 Nic Wise 5/23/2009 #5

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

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

大多数情况下,这是由于不知道何时或是否会调用它。处置方法要好得多,特别是如果您直接使用或处置。

使用是好的。使用它:)

评论

2赞 ℍ ℍ 5/23/2009
您应该点击thecoop回答中的链接。是的,使用/处置更好,但 Disposable 类绝对应该同时实现两者。
1赞 Nic Wise 5/23/2009
有趣的是,我从微软读到的所有文档——例如框架设计指南——都说永远不要使用析构函数。始终使用 IDisposable。
5赞 ℍ ℍ 5/23/2009
只要区分使用类和编写类,再读一遍。
0赞 Alex Nolasco 2/28/2013
stackoverflow.com/questions/2605412/......可能会有所帮助
41赞 6 revsDave Black #6

请注意,任何 IDisposable 实现都应遵循以下模式(恕我直言)。我根据几个优秀的 .NET “神”的信息开发了此模式:.NET Framework 设计指南(请注意,MSDN 出于某种原因没有遵循此指南!.NET Framework 设计指南由 Krzysztof Cwalina(当时的 CLR 架构师)和 Brad Abrams(我相信当时的 CLR 项目经理)和 Bill Wagner([有效的 C#] 和 [更有效的 C#])编写(只需在 Amazon.com 上查看这些内容:

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

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

#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赞 supercat 2/24/2012
Microsoft 似乎喜欢在 disposed 方法的末尾设置“disposed”标志,但这对我来说似乎是错误的。对“Dispose”的冗余调用应该什么都不做;虽然人们通常不会期望 Dispose 被递归调用,但如果尝试 Dispose 对象因构造或其他操作期间发生的异常而处于无效状态,则可能会发生这种情况。我认为在非虚拟包装器函数中使用整数标志会更安全。Interlocked.ExchangeIsDisposed
1赞 Didier A. 6/22/2012
@DaveBlack:如果您的基类不使用非托管资源,但派生类使用,该怎么办?那么你是否必须在派生类中实现终结器呢?如果是这样,如果您无权访问源代码,您如何知道基类尚未实现它?
0赞 Piotr Perak 8/14/2014
为什么说带有终结器的对象会进入第 2 代?他们上升了一代,但可以在第 1 代死亡。我用 sos 确认了。
12赞 pm100 1/14/2010 #7

没有人回答是否应该实现 IDisposable 的问题,即使你不需要它。

简短的回答 : 否

长答案:

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

  • 它从经验中获取它们(例如套接字类)
  • 它记录了
  • 他们很谨慎,可以看到该类实现了 IDisposable

因此,通过实现 IDisposable,您告诉开发人员(至少是一些)这个类包含了一些必须发布的东西。他们将使用“使用”——但也有其他情况下无法使用(对象的范围不是本地的);在其他情况下,他们将不得不开始担心对象的寿命 - 我肯定会担心。但这不是必需的

您实现 Idisposable 以使他们能够使用 using,但除非您告诉他们,否则他们不会使用 use。

所以不要这样做

评论

1赞 pm100 1/14/2010
关键是开发人员必须编写所有调用,以便在所有代码路径中进行处置,从而导致取消引用。例如,如果我将一个实例放在字典中,当我从字典中删除条目时,我必须调用 dispose。在这种情况下不需要很多麻烦 - 不需要处理对象
3赞 Kevin P. Rice 7/20/2011
@pm100 Re:不必要地实现 IDisposable -- codeproject.com/KB/dotnet/idisposable.aspx 上有一篇详细的文章,其中讨论了一些您可能想要考虑这个问题的罕见情况(我敢肯定,这种情况非常罕见)。简而言之:如果可以预见将来或派生对象中对 IDisposable 的需求,那么可以考虑在基类中将 IDisposable 实现为“no-op”,以避免某些派生对象需要处置而其他对象不需要处理的“切片”问题。
23赞 4 revs, 2 users 88%Dave Black #8

我同意 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. 如果您的类使用强引用订阅事件。您需要取消注册/脱离事件。在尝试取消注册/分离它们之前,始终首先确保它们不是空的!

  5. 您的类包含上述内容的任意组合...

除了使用 COM 对象和必须使用 Marshal.ReleaseComObject() 之外,建议的替代方法是使用 System.Runtime.InteropServices.SafeHandle 类。

BCL(基类库团队)在这里有一篇关于它的很好的博客文章 http://blogs.msdn.com/bclteam/archive/2005/03/16/396900.aspx

需要注意的一个非常重要的注意事项是,如果您正在使用 WCF 并清理资源,则几乎总是应该避免使用“using”块。MSDN上有很多博客文章,关于为什么这是一个坏主意。我也在这里发布了它 - 不要将“using()”与 WCF 代理一起使用

评论

3赞 Didier A. 6/22/2012
我相信还有第 5 种情况:如果您的类使用强引用订阅事件,那么您应该实现 IDisposable 并从 Dispose 方法中的事件中注销自己。
0赞 Dave Black 8/29/2012
嗨,滴滴。是的,你是对的。我忘了那个。我已经修改了我的答案,将其作为一个案例。谢谢。
0赞 Gonen I 11/20/2014
dispose 模式的 MSDN 文档添加了另一种情况:“考虑在本身不包含非托管资源或一次性对象但可能具有具有不托管资源或一次性对象的子类型的类上实现基本 Dispose 模式。System.IO.Stream 类就是一个很好的例子。虽然它是一个抽象的基类,不包含任何资源,但它的大多数子类都包含,因此,它实现了这种模式。
126赞 Jordão 1/30/2010 #9

要实施的官方模式很难理解。我相信这个更好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
  }

}

评论

7赞 HuseyinUslu 3/17/2011
虽然我想注意的一件事是它并不能阻止第二次被调用。
6赞 Jordão 3/18/2011
@HuseyinUslu:这只是模式的本质。您当然可以添加一个标志并进行相应的检查。disposed
0赞 Didier A. 6/22/2012
@Jordão:除了 isDisposed 标志的实现以这种方式变得更加复杂。实际上,我什至想不出一种方法可以添加它并保持图案漂亮。你能用你的模式展示一种方法吗?
2赞 Jordão 6/22/2012
@didibus:添加标志很简单,在处置前检查,处置后设置。在这里寻找这个想法。您还应该在类的任何方法之前检查该标志。 有道理吗?它复杂吗?disposed
2赞 softwarematter 3/28/2010 #10

来自 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);
         }
      }
   }
}
4赞 Dave Black 3/9/2011 #11

另一个答案的某些方面略有错误,原因有 2 个:

第一

using(NoGateway objNoGateway = new NoGateway())

实际上等同于:

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

这听起来可能很荒谬,因为“new”运算符不应该返回“null”,除非您有 OutOfMemory 异常。但请考虑以下情况: 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 检查,在尝试对 null 对象调用 Dispose 时,代码不会爆炸。

您的回复不准确的第二个原因是因为以下标准:

当 GC 摧毁您的对象时,会调用终结器

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

我已经在我的博客上发布了 Dispose Pattern 的正确实现:How to Properly Implement the Dispose Pattern

评论

2赞 Cœur 7/15/2013
你确定写作和吗?NoGateway = new NoGateway();NoGateway != null
13赞 pm100 7/21/2011 #12

使用 lambda 而不是 IDisposable。

我从来没有对整个 using/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赞 supercat 12/14/2011
将东西包装在 lambda 中可能是一个很好的方法,但它有局限性。事实上,对于一个类的所有使用者都会使用“using”块的情况来说,这还不错,但它不允许方法将 IDisposable 存储在类字段中(直接存储,或类似迭代器)的情况。
0赞 pm100 12/20/2011
@supercat你可以争辩说,不允许存储占用资源的东西是一件好事。我在这里提出的借用模型迫使你对资源的使用保持精益
0赞 supercat 12/20/2011
这可能是一件好事,但它也可能使一些非常合理的操作变得非常困难。例如,假设数据库读取器类型不是实现 IEnumerable<T>,而是公开一个方法,在每条记录上调用指定的委托。给定两个这样的对象,它们都将按排序顺序返回数据,如何输出一个集合中存在的所有项,而不是另一个集合?如果实现了类型,则可以执行合并操作,但这不适用于 。DoForAll(Action<T>) where T:IComparable<T>IEnumerable<T>DoForAll
0赞 supercat 12/20/2011
我能想到合并两个集合而不必首先将一个集合全部复制到其他结构中的唯一方法是使用两个线程,这比简单地使用几个 IEnumerable 并小心地释放它们更耗费资源。DoForAll
5赞 Andrei Krasutski 8/28/2014 #13

处置模式:

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();
        }
    }
}