Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 正确使用IDisposable接口_C#_.net_Garbage Collection_Idisposable - Fatal编程技术网

C# 正确使用IDisposable接口

C# 正确使用IDisposable接口,c#,.net,garbage-collection,idisposable,C#,.net,Garbage Collection,Idisposable,我从阅读中了解到,IDisposable接口的“主要”用途是清理非托管资源 对我来说,“非托管”意味着数据库连接、套接字、窗口句柄等。但是,我见过一些代码,其中实现了Dispose()方法来释放托管资源,这对我来说似乎是多余的,因为垃圾回收器应该为您处理这些 例如: public class MyCollection : IDisposable { private List<String> _theList = new List<String>(); pr

我从阅读中了解到,
IDisposable
接口的“主要”用途是清理非托管资源

对我来说,“非托管”意味着数据库连接、套接字、窗口句柄等。但是,我见过一些代码,其中实现了
Dispose()
方法来释放托管资源,这对我来说似乎是多余的,因为垃圾回收器应该为您处理这些

例如:

public class MyCollection : IDisposable
{
    private List<String> _theList = new List<String>();
    private Dictionary<String, Point> _theDict = new Dictionary<String, Point>();

    // Die, clear it up! (free unmanaged resources)
    public void Dispose()
    {
        _theList.clear();
        _theDict.clear();
        _theList = null;
        _theDict = null;
    }
公共类MyCollection:IDisposable
{
私有列表_theList=新列表();
专用词典_theDict=新词典();
//死吧,清理它!(释放非托管资源)
公共空间处置()
{
_清除列表();
_theDict.clear();
_列表=空;
_theDict=null;
}
我的问题是,这是否会使垃圾收集器释放MyCollection使用的内存的速度比正常情况下快


编辑:到目前为止,人们已经发布了一些使用IDisposable清理非托管资源(如数据库连接和位图)的好例子。但是假设上面代码中的
\u列表
包含一百万个字符串,并且您希望现在就释放内存,而不是等待垃圾收集器。您会这样做吗上面的代码实现了这一点?

是的,这段代码是完全冗余和不必要的,它不会让垃圾收集器做任何本来不会做的事情(即,一旦MyCollection的实例超出范围),尤其是
.Clear()
调用

对编辑的回答:有点。如果我这样做:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has no Dispose() method
    instance.FillItWithAMillionStrings();
}

// 1 million strings are in memory, but marked for reclamation by the GC
出于内存管理的目的,其功能与此相同:

public void WasteMemory()
{
    var instance = new MyCollection(); // this one has your Dispose()
    instance.FillItWithAMillionStrings();
    instance.Dispose();
}

// 1 million strings are in memory, but marked for reclamation by the GC

如果您真的需要立即释放内存,请调用
GC.Collect()
。不过,这里没有理由这样做。内存将在需要时释放。

IDisposable
通常用于利用
using
语句,并利用一种简单的方法对托管对象进行确定性清理

public class LoggingContext : IDisposable {
    public Finicky(string name) {
        Log.Write("Entering Log Context {0}", name);
        Log.Indent();
    }
    public void Dispose() {
        Log.Outdent();
    }

    public static void Main() {
        Log.Write("Some initial stuff.");
        try {
            using(new LoggingContext()) {
                Log.Write("Some stuff inside the context.");
                throw new Exception();
            }
        } catch {
            Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
        } finally {
            Log.Write("Some final stuff.");
        }
    }
}

如果
MyCollection
无论如何都要进行垃圾收集,那么您不需要对其进行处理。这样做只会对CPU造成不必要的干扰,甚至可能使垃圾收集器已经执行的某些预先计算的分析无效

我使用
IDisposable
来确保线程与非托管资源一起被正确处置

编辑回应斯科特的评论:

GC性能指标仅在调用[sic]GC.Collect()时受到影响”

从概念上讲,GC维护对象引用图的视图,以及从线程堆栈帧对其进行的所有引用。此堆可能相当大,并跨越许多内存页。作为一种优化,GC缓存其对不太可能经常更改的页的分析,以避免不必要地重新扫描页。GC收到当页面中的数据发生更改时,内核会发出通知,因此它知道页面脏了,需要重新扫描。如果集合在Gen0中,则页面中的其他内容可能也在更改,但在Gen1和Gen2中这种情况不太可能发生。有趣的是,对于将GC移植到Mac i的团队来说,Mac OS X中没有这些挂钩n以使Silverlight插件在该平台上工作

反对不必要的资源处置的另一点:设想一个进程正在卸载的情况。还设想该进程已经运行了一段时间。该进程的许多内存页可能已经交换到磁盘。至少它们不再位于一级或二级缓存中。在这种情况下,没有f点或正在卸载以将所有数据和代码页交换回内存的应用程序,以便在进程终止时“释放”操作系统将释放的资源。这适用于托管资源,甚至某些非托管资源。否则,必须仅释放使非后台线程保持活动状态的资源这一进程将继续下去

现在,在正常执行期间,必须正确清理临时资源(正如@fezmonkey指出的数据库连接、套接字、窗口句柄),以避免非托管内存泄漏。这些都是必须处理的事情。如果您创建了某个拥有线程的类(我所说的owns是指它创建了它,因此负责确保它停止,至少按照我的编码风格),那么该类很可能必须实现
IDisposable
,并在
Dispose
期间中断线程


NET framework使用
IDisposable
接口作为信号,甚至警告开发人员必须处理此类。我想不出框架中有任何类型实现
IDisposable
(不包括显式接口实现)其中Dispose是可选的。

Dispose模式的目的是提供一种机制来清理托管和非托管资源,何时清理取决于Dispose方法的调用方式。在您的示例中,Dispose的使用实际上并没有做任何与Dispose相关的事情,因为清除列表对该方法没有影响同样,将变量设置为null的调用也不会对GC产生影响

有关如何实现Dispose模式的更多详细信息,您可以查看以下内容,但基本上如下所示:

public class SimpleCleanup : IDisposable
{
    // some fields that require cleanup
    private SafeHandle handle;
    private bool disposed = false; // to detect redundant calls

    public SimpleCleanup()
    {
        this.handle = /*...*/;
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // Dispose managed resources.
                if (handle != null)
                {
                    handle.Dispose();
                }
            }

            // Dispose unmanaged managed resources.

            disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}
这里最重要的方法是Dispose(bool),它实际上在两种不同的情况下运行:

  • disposing==true:该方法已被用户的代码直接或间接调用。托管和非托管资源都可以被释放
  • disposing==false:该方法已由运行时从终结器内部调用,您不应引用其他对象。仅
    public void Cleanup()
    
    public void Shutdown()
    
    public void Dispose()
    
    public interface IDisposable
    {
       void Dispose()
    }
    
    public void Dispose()
    {
       Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
    }
    
    public void Dispose()
    {
       //Free unmanaged resources
       Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
    
       //Free managed resources too
       if (this.databaseConnection != null)
       {
          this.databaseConnection.Dispose();
          this.databaseConnection = null;
       }
       if (this.frameBufferImage != null)
       {
          this.frameBufferImage.Dispose();
          this.frameBufferImage = null;
       }
    }
    
    ~MyObject()
    {
        //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to
        Dispose(); //<--Warning: subtle bug! Keep reading!
    }
    
    public void Dispose()
    {
       //Free unmanaged resources
       Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle);
    
       //Free managed resources too
       if (this.databaseConnection != null)
       {
          this.databaseConnection.Dispose(); //<-- crash, GC already destroyed it
          this.databaseConnection = null;
       }
       if (this.frameBufferImage != null)
       {
          this.frameBufferImage.Dispose(); //<-- crash, GC already destroyed it
          this.frameBufferImage = null;
       }
    }
    
    protected void Dispose(Boolean disposing)
    
    protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects)
    {
       //Free unmanaged resources
       Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
    
       //Free managed resources too, but only if I'm being called from Dispose
       //(If I'm being called from Finalize then the objects might not exist
       //anymore
       if (itIsSafeToAlsoFreeManagedObjects)  
       {    
          if (this.databaseConnection != null)
          {
             this.databaseConnection.Dispose();
             this.databaseConnection = null;
          }
          if (this.frameBufferImage != null)
          {
             this.frameBufferImage.Dispose();
             this.frameBufferImage = null;
          }
       }
    }
    
    public void Dispose()
    {
       Dispose(true); //I am calling you from Dispose, it's safe
    }
    
    ~MyObject()
    {
       Dispose(false); //I am *not* calling you from Dispose, it's *not* safe
    }
    
    public override void Dispose()
    {
        try
        {
            Dispose(true); //true: safe to free managed resources
        }
        finally
        {
            base.Dispose();
        }
    }
    
    protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize)
    {
       //Free unmanaged resources
       Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //<--double destroy 
       ...
    }
    
    public void Dispose()
    {
       Dispose(true); //I am calling you from Dispose, it's safe
       GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later
    }
    
    ~MyObject()
    {
       //Free unmanaged resources
       Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle);
    
       //A C# destructor automatically calls the destructor of its base class.
    }
    
    class MyClass : IDisposable {
        // ...
    
        #region IDisposable Members and Helpers
        private bool disposed = false;
    
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
    
        private void Dispose(bool disposing) {
            if (!this.disposed) {
                if (disposing) {
                    // cleanup code goes here
                }
                disposed = true;
            }
        }
    
        ~MyClass() {
            Dispose(false);
        }
        #endregion
    }
    
    public static void Indented(this Log log, Action action)
    {
        log.Indent();
        try
        {
            action();
        }
        finally
        {
            log.Outdent();
        }
    }
    
    Log.Write("Message at the top");
    Log.Indented(() =>
    {
        Log.Write("And this is indented");
    
        Log.Indented(() =>
        {
            Log.Write("This is even more indented");
        });
    });
    Log.Write("Back at the outermost level again");
    
    using( MiniTx tx = new MiniTx() )
    {
        // code that might not work.
    
        tx.Commit();
    } 
    
    using( LogTimer log = new LogTimer("MyCategory", "Some message") )
    {
        // code to time...
    }
    
    Public Class LargeStuff Implements IDisposable Private _Large as string() 'Some strange code that means _Large now contains several million long strings. Public Sub Dispose() Implements IDisposable.Dispose _Large=Nothing End Sub
    public class SomeClass : IDisposable
        {
            /// <summary>
            /// As usually I don't care was object disposed or not
            /// </summary>
            public void SomeMethod()
            {
                if (_disposed)
                    throw new ObjectDisposedException("SomeClass instance been disposed");
            }
    
            public void Dispose()
            {
                Dispose(true);
            }
    
            private bool _disposed;
    
            protected virtual void Dispose(bool disposing)
            {
                if (_disposed)
                    return;
                if (disposing)//we are in the first call
                {
                }
                _disposed = true;
            }
        }
    
    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.
        }
    }