Ori*_*rds 126 .net dispose idisposable
.NET IDisposable Pattern 意味着如果您编写终结器并实现IDisposable,则终结器需要显式调用Dispose.这是合乎逻辑的,而且在极少数情况下我总是会做终结器的保证.
但是,如果我这样做会发生什么:
class Foo : IDisposable
{
public void Dispose(){ CloseSomeHandle(); }
}
Run Code Online (Sandbox Code Playgroud)
并且不要实现终结器或任何东西.框架会为我调用Dispose方法吗?
是的,我意识到这听起来很愚蠢,而且所有的逻辑都暗示它不会,但我总是有两件事让我不确定.
几年前有人曾告诉我,事实上它会这样做,而且那个人有"非常了解他们的东西"的良好记录.
编译器/框架根据您实现的接口(例如:foreach,扩展方法,基于属性的序列化等)执行其他"神奇"操作,因此这也可能是"魔术".
虽然我已经阅读了很多关于它的内容,并且有很多暗示的内容,但我从来没有能够找到这个问题的肯定是或否答案.
Xia*_*ian 112
.Net垃圾收集器在垃圾收集上调用对象的Object.Finalize方法.通过默认情况下这并没有什么,如果你想释放更多的资源必须overidden.
如果要释放资源,则不会自动调用Dispose并且必须显式调用Dispose ,例如在"using"或"try finally"块中
有关更多信息,请参阅http://msdn.microsoft.com/en-us/library/system.object.finalize.aspx
Cor*_*Foy 63
我想强调Brian在评论中的观点,因为它很重要.
终结器不是像C++中那样的确定性析构函数.正如其他人所指出的那样,没有什么时候会被称为保证,而事实上,如果你有足够的内存,如果将永远被调用.
但是关于终结器的坏处是,正如Brian所说,它会使你的对象在垃圾收集中存活下来.这可能很糟糕.为什么?
正如您可能知道或不知道的那样,GC分为几代 - Gen 0,1和2,以及大对象堆.Split是一个松散的术语 - 你得到一块内存,但是有一些指向Gen 0对象的起点和终点.
思考过程是你可能会使用很多短暂的物体.因此,对于GC来说,这些应该是简单快速的 - Gen 0对象.因此,当存在内存压力时,它首先做的是Gen 0集合.
现在,如果那不能解决足够的压力,那么它会返回并进行第1代扫描(重做第0代),然后如果仍然不够,则执行第2代扫描(重做第1代和第0代).因此,清理长寿命对象可能需要一段时间并且相当昂贵(因为在操作期间您的线程可能会被挂起).
这意味着,如果您执行以下操作:
~MyClass() { }
Run Code Online (Sandbox Code Playgroud)
无论如何,您的对象都将存在于第2代.这是因为GC无法在垃圾回收期间调用终结器.所以必须最终确定的对象被移动到一个特殊的队列,由另一个线程清理掉(终结器线程 - 如果你杀了它就会发生各种坏事).这意味着您的对象会更长时间地挂起,并可能会强制更多垃圾收集.
所以,所有这一切只是为了让你想要使用IDisposable来尽可能地清理资源,并认真尝试找到使用终结器的方法.这符合您的应用程序的最佳利益.
And*_*rew 31
这里已经有很多很好的讨论了,我在派对上有点迟了,但我想自己补充几点.
class SomeObject : IDisposable {
IntPtr _SomeNativeHandle;
FileStream _SomeFileStream;
// Something useful here
~ SomeObject() {
Dispose(false);
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
if(disposing) {
GC.SuppressFinalize(this);
//Because the object was explicitly disposed, there will be no need to
//run the finalizer. Suppressing it reduces pressure on the GC
//The managed reference to an IDisposable is disposed only if the
_SomeFileStream.Dispose();
}
//Regardless, clean up the native handle ourselves. Because it is simple a member
// of the current instance, the GC can't have done anything to it,
// and this is the onlyplace to safely clean up
if(IntPtr.Zero != _SomeNativeHandle) {
NativeMethods.CloseHandle(_SomeNativeHandle);
_SomeNativeHandle = IntPtr.Zero;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是简单的版本,但有很多细微差别可以让你在这种模式上绊倒.
在我看来,完全避免使用任何直接包含可能需要最终确定的一次性引用和本机资源的类型要好得多.SafeHandles提供了一种非常干净的方法,通过将本机资源封装到一次性内部提供自己的最终化(以及在P/Invoke中删除窗口,其中本机句柄可能由于异步异常而丢失)等许多其他好处.
简单地定义一个SafeHandle使这个简单:
private class SomeSafeHandle
: SafeHandleZeroOrMinusOneIsInvalid {
public SomeSafeHandle()
: base(true)
{ }
protected override bool ReleaseHandle()
{ return NativeMethods.CloseHandle(handle); }
}
Run Code Online (Sandbox Code Playgroud)
允许您将包含类型简化为:
class SomeObject : IDisposable {
SomeSafeHandle _SomeSafeHandle;
FileStream _SomeFileStream;
// Something useful here
public virtual void Dispose() {
_SomeSafeHandle.Dispose();
_SomeFileStream.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
我不这么认为.您可以控制何时调用Dispose,这意味着您可以在理论上编写处理代码,以对(例如)其他对象的存在进行假设.您无法控制何时调用终结器,因此终结器会代表您自动调用Dispose.
编辑:我去了测试,只是为了确保:
class Program
{
static void Main(string[] args)
{
Fred f = new Fred();
f = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("Fred's gone, and he's not coming back...");
Console.ReadLine();
}
}
class Fred : IDisposable
{
~Fred()
{
Console.WriteLine("Being finalized");
}
void IDisposable.Dispose()
{
Console.WriteLine("Being Disposed");
}
}
Run Code Online (Sandbox Code Playgroud)
不是你所描述的情况,但 GC 会为你调用终结器(如果你有的话)。
然而。下一次垃圾收集时,对象将进入终结队列,而不是被收集,所有内容都会被收集,然后调用终结器。之后的下一个集合将被释放。
根据应用程序的内存压力,您可能有一段时间没有用于该对象生成的 gc。因此,在文件流或数据库连接的情况下,您可能需要等待一段时间才能在终结器调用中释放非托管资源一段时间,从而导致一些问题。
归档时间: |
|
查看次数: |
29288 次 |
最近记录: |