请考虑以下代码:
namespace DisposeTest
{
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Calling Test");
Test();
Console.WriteLine("Call to Test done");
}
static void Test()
{
DisposeImplementation di = new DisposeImplementation();
}
}
internal class DisposeImplementation : IDisposable
{
~DisposeImplementation()
{
Console.WriteLine("~ in DisposeImplementation instance called");
}
public void Dispose()
{
Console.WriteLine("Dispose in DisposeImplementation instance called");
}
}
}
即使我在Test();
调用之后放置了一个等待循环,Dispose也永远不会被调用。所以这很糟糕。我想编写一个简单易用的类,以确保清理所有可能的资源。我不想把这个责任交给我班上的用户。
可能的解决方案:使用using
,或者调用Dispose我自己(基本相同)。我可以强制用户使用吗?或者我可以强制调用处理吗?
GC.Collect();
之后调用Test();
也不起作用。
将di
置于null
也不会调用Dispose。解构函数可以工作,因此当对象退出时会被解构Test()
好的,现在很清楚了!
谢谢大家的回答!我会在评论中添加警告!
答案 0 :(得分:46)
应该提出几个要点来解决OP的问题:
using()
块中。<强>终结:强> 一些开发人员将其称为析构函数。 事实上它甚至在当前C# 4.0 Language Spec (section 1.6.7.6) 的ECMA-334 spec和之前的版本中称为析构函数。幸运的是,第4版(2006年6月)正确定义了第8.7.9节中的终结器,并尝试在第17.12节中澄清两者之间的混淆。应该注意的是,在.NET Framework中,传统上称为析构函数和析构函数/终结符之间存在重要的内部差异(无需在此处进行详细介绍)。
GC.SuppressFinalize()
时,.NET Framework才会调用它。GC.Collect(2)
来强制它。<强>最后确定:强> 最终化是.NET Framework处理“优雅”清理和释放资源的方法。
虽然这肯定是您要求的更多信息,但它提供了有关工作方式以及工作原理的背景知识。有些人会争辩说他们不应该担心在.NET中管理内存和资源,但这并没有改变它需要完成的事实 - 我不认为它会在不久的将来消失。
答案 1 :(得分:19)
我想写一个类 简单易用 确保每一个可能的 资源被清理干净。我不想要 把责任交给用户 我班上的。
你做不到。内存管理根本不是为了容纳非特定内存的资源而构建的。
IDisposable模式旨在让开发人员在完成对象时告诉对象,而不是让内存管理通过使用引用计数之类的东西来解决这个问题。
您可以将Finalizer用作未能正确处理对象的用户的后备,但它不能很好地作为清理对象的主要方法。为了顺利工作,应妥善处理对象,这样就不需要调用更昂贵的终结器。
答案 2 :(得分:13)
所有答案都是(或多或少)正确,这是一个例子:
static void Test()
{
using (DisposeImplementation di = new DisposeImplementation())
{
// Do stuff with di
}
}
手动调用Dispose
也会有效,但using
语句的优点是当你离开控制块时也会处理该对象,因为抛出了异常。
您可以添加一个处理资源处理的终结器,以防有人“忘记”使用IDisposable接口:
public class DisposeImplementation : 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
}
~DisposeImplementation()
{
Dispose(false);
}
}
有关其他信息,请参阅this question。但是,这只是补偿了没有正确使用你的课程的人:)我建议你给Finalizer添加一个很大的Debug.Fail()
调用,以警告开发者他们的错误。
如果您选择实施该模式,您会看到GC.Collect()
将触发处置。
答案 3 :(得分:7)
将其用作课程的模式/模板
public class MyClass : IDisposable
{
private bool disposed = false;
// 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.
private 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.
......
}
// Call the appropriate methods to clean up
// unmanaged resources here.
// If disposing is false,
// only the following code is executed.
...........................
// Note disposing has been done.
disposed = true;
}
}
// 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.
~MyClass()
{
// Do not re-create Dispose clean-up code here.
// Calling Dispose(false) is optimal in terms of
// readability and maintainability.
Dispose(false);
}
}
当然,正如其他人所提到的,不要忘记using(...){}
阻止。
答案 4 :(得分:2)
您必须显式调用Dispose
或将对象包装在using
语句中。例如:
using (var di = new DisposeImplementation())
{
}
可能的解决方案:使用或呼叫 处理我自己(基本相同)。
使用using
与调用Dispose
块中的finally
相同。
答案 5 :(得分:1)
你应该自己处理它,要么调用Dispose
方法,要么使用using
。记住,它不是一个解构者!
如果你不能相信你班级的用户正确处理资源,他们可能会以其他方式陷入困境。
答案 6 :(得分:1)
Dispose不会自动调用。您需要使用using
子句来包装使用或手动调用它。
请参阅http://msdn.microsoft.com/en-us/library/aa664736%28VS.71%29.aspx
只是为了抢占你可能有的另一个想法:你不能从析构函数中调用dispose
......我前段时间在一个项目中试过这个。