【问题标题】:Safe to call managed resource from Finalizer? (if i check null)从终结器调用托管资源安全吗? (如果我检查空)
【发布时间】:2012-07-06 14:01:01
【问题描述】:

打电话不安全吗:

component.Dispose(); (如果我检查 null)

如果我将代码更改为此,来自终结器:

~MyResource()
{
    Dispose();
}
public void Dispose()
{
 // Check to see if Dispose has already been called.
        if(!this.disposed)
        {
            if(component != null) component.Dispose();   // !!! //

            CloseHandle(handle);
            handle = IntPtr.Zero;

            disposed = true;

        }
    GC.SuppressFinalize(this);
}

我知道这行得通 - 但它安全吗?

(来自下面的示例?)

代码示例:(修改代码之前)

http://msdn.microsoft.com/en-us/library/system.idisposable.dispose.aspx

using System;
using System.ComponentModel;

// The following example demonstrates how to create
// a resource class that implements the IDisposable interface
// and the IDisposable.Dispose method.

public class DisposeExample
{
// A base class that implements IDisposable.
// By implementing IDisposable, you are announcing that
// instances of this type allocate scarce resources.
public class MyResource: IDisposable
{
    // Pointer to an external unmanaged resource.
    private IntPtr handle;
    // Other managed resource this class uses.
    private Component component = new Component();
    // Track whether Dispose has been called.
    private bool disposed = false;

    // The class constructor.
    public MyResource(IntPtr handle)
    {
        this.handle = handle;
    }

    // Implement IDisposable.
    // Do not make this method virtual.
    // A derived class should not be able to override this method.
    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.
        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.
                component.Dispose();
            }

            // Call the appropriate methods to clean up
            // unmanaged resources here.
            // If disposing is false,
            // only the following code is executed.
            CloseHandle(handle);
            handle = IntPtr.Zero;

            // Note disposing has been done.
            disposed = true;

        }
    }

    // Use interop to call the method necessary
    // to clean up the unmanaged resource.
    [System.Runtime.InteropServices.DllImport("Kernel32")]
    private extern static Boolean CloseHandle(IntPtr handle);

    // 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.
    ~MyResource()
    {
        // Do not re-create Dispose clean-up code here.
        // Calling Dispose(false) is optimal in terms of
        // readability and maintainability.
        Dispose(false);
    }
  }
  public static void Main()
  {
    // Insert code here to create
    // and use the MyResource object.
  }
}

【问题讨论】:

  • +1 从来没见过有人这么好地评论代码。
  • Finalizers and Dispose的可能重复
  • @Niklas 请阅读this answer,一切都解释得很好。
  • @ken2k - 感谢您的阅读,但这不是我直接问题的答案!

标签: c# dispose finalizer


【解决方案1】:

在不了解“组件”的实现细节的情况下,很难确定这样做是否安全。对象的终结是无序的并且不尊重包含层次结构,因此虽然“组件”实例可能不是null,但在您调用Dispose 时它可能已经终结。通常Dispose 方法在编写时并没有考虑到这种类型的安全性,因为它们只希望在对象完成之前被调用。

在你甚至想考虑做一些聪明的事情之前,比如在终结器中触摸其他对象,请阅读 Chris Brumme's blog 关于该主题的帖子; this one is a good starting point.

通常,除非在非常特殊的情况下,并且您知道包含对象的 Dispose 方法即使在它已完成时被调用,其行为也符合预期,我假设您所询问的模式是不安全的并且将坚持传递bool disposing 参数的推荐模式,并且仅在true 时才接触托管对象。

【讨论】:

  • 谢谢,如果我只使用内置类型的托管资源,比如数据上下文。然后我想它不需要终结器,只需 dispose() 方法就足够了! (防止数据上下文使用它的终结器)
【解决方案2】:

在 .net 中,可以通过任何方式访问的任何对象都保证存在,可以访问此类对象的字段,并且可以分派对该对象的属性访问或方法调用,就像在任何其他情况下一样。唯一不能保证的是系统是否已经在引用的对象上运行Finalize 方法。完全有可能由于系统已经在对象上运行了Finalize,以前会做一些有用的事情的方法将不再能够这样做,但这是对象的Finalize() 例程的功能并且与垃圾收集器本身无关。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2012-12-06
  • 1970-01-01
  • 2010-12-07
  • 2023-04-06
  • 1970-01-01
  • 2015-10-20
  • 1970-01-01
相关资源
最近更新 更多