2014-01-21 38 views
1

考虑我们正在使用一些非托管资源。为什么析构函数不足以实现IDisposable?

最常见的方法是:

//With IDisposable 
using (MemoryStream memoryStream = new MemoryStream()) 
{ 
    //Operate with memory stream 
} 

但是我们不能写以下?

//With destructor called at the end of a block 
{ 
    MemoryStream memoryStream = new MemoryStream(); 
    //Operate with memory stream 
} 
  • 什么是IDisposable的主要目标是什么?

  • 它只是最终化逻辑分离的设计模式吗?

  • 是吗?例如,Java,提供类似的东西吗?

+0

您可以在Java 7中的AutoCloseable上使用try-with-resources块:try(AutoCloseable resource = ...){...}。在Java中,析构函数不能用于此目的,因为它们可以在GC线程的任意时间被调用。 – Njol

+1

Java提供了两个终结器和类似于'using'的东西。 Java中的终结器在C#中是不可实现的,它们唯一的用法就是在那里以防万一。 –

+2

析构函数不在块的末尾调用。它在不确定的时间被呼叫,由GC决定。即使在C++中,如果你这样做了,析构函数也不会在块的结尾被调用。你在堆上分配一个对象,它的生命周期不限于它的范围。另一方面,在使用块的末尾调用Dispose IS。 – svinja

回答

2
  • 什么是IDisposable的主要目标是什么?

  • 它只是最终化逻辑分离的设计模式吗?

它的目标很简单,是一个标准的接口来调用这个逻辑。为了提高效率,recommended implementation将处理手动处理(Dispose(true))与处理终止符(Dispose(false))中调用的Dispose分离开来 - 从终结器内部处理内部字段是多余的,因为由于终结器不能手动调用,所以我们知道它们必须已被收集。

手动处理是必需的,因为垃圾收集不是直接的(并且不能强制单独收集单个对象)。这不仅仅是一个效率问题,因为非托管资源可能不支持多个访问,所以如果第一个还没有收集,另一个访问它的对象将会失败。集合甚至没有保证,因为Ricibob显示,将代码放入一个明确的范围内并不会阻止外部对象获取自己的引用。

  • 并执行,例如的Java,提供类似的东西?

using仅仅是一个自动实现的“尝试......终于”块调用预先定义的方法。

Java包括此功能作为extension of the try statement itself。这与using的作用相同,除了它还允许您添加自己的catchfinally块,而无需将其包装在其他try块中。

Pythoncontext managers这是一个更灵活的版本。上下文管理者可以定义特定的异常处理以及在finally,并可能返回不同的对象比在传递 - 也就是说,这将是可能的:

with CustomDisposer(MemoryStream()) as memoryStream: 

其中CustomDisposer对象负责处置实现,但它返回MemoryStream作为要分配给memoryStream变量的资源。

红宝石yield语句,它允许一个功能wrap a code block和需要给一个参数来将挡,这样你就可以通过给定对象传递给块,然后调用部署在(相当于finallyensure实现这一点:

def using(o) 
    yield o 
ensure 
    o.dispose 
end 

using MemoryStream.new do |memoryStream| 
    #Operate with memory stream 
end 

当然,因为这是一个函数定义的一部分,没有需要专用using功能 - 它可以在例如MemoryStream.Open方法来直接实现。

+0

我不会推荐在.net 2.0或更高版本上实现。基于SafeHandle的类通常是一个更好的主意。 – CodesInChaos

2

析构函数不会在块的末尾被调用,但每当GC破坏对象。发生这种情况时你无法控制。有时你需要这种控制。那时候IDisposable进场了。

1

考虑:

var sc = new StreamConsumer(); 
{ 
    var memoryStream = new MemoryStream(); 
    //Operate with memory stream 
    sc.Stream = memoryStream; 
} 
sc.DoStuffWithStream(); 

这GC的工作,以确定我们实际上用的MemoryStream完成,它可以被摧毁。这并不总是直截了当(正如C++开发人员所知道的那样)。我们不知道GC何时会这样做 - 它不及时确定。

使用/ IDisposable不是关于对象销毁,而是关于以及时确定性方式控制对象对资源的访问。当我们申请一个使用者说在这个声明之外,我知道资源(文件,内存映射文件,套接字等)是免费的并且可以被其他对象使用。如果我尝试访问一个处置对象(在它的使用范围外),因为还有其他对象持有引用 - 那么我会得到一个异常。

1

注意到终结器(descructor)实际上并不能保证完全运行也很重要。通常,当实现一个终结器时,它所做的全部工作就是调用Dispose会计,以便消费者忘记将该对象放在使用块中的机会。如果您怀疑某个对象未释放内存泄漏,则可以实施此模式。

微软甚至放弃这种做法的名称:http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx

的要点是:在C#中,你应该总是使用IDisposable接口。

0

GC的基本目的是确保对任何对象的引用永远不会变成除了引用该对象之外的任何其他对象。因此,如果它能够看到任何可能的方式,通过它们可以访问它们的引用,它必须保持对象。因为存在强根引用的对象通常是“有用的”,并且没有强根引用存在的对象通常不是“有用的”,所以垃圾收集器也可以在一定程度上标识无用的对象并且应该具有对它们的所有引用淘汰。不幸的是,即使GC具有完美的响应能力,并且可以即时识别出对任何给定对象的最后一个强引用何时消失,但这并不意味着它可以可靠地识别无用对象。如果X对Y有强烈的引用,那通常意味着如果某事物对X感兴趣,则某事物将对Y感兴趣。然而,在某些情况下,它将意味着Y有兴趣接收来自X的某种通知。假设X已注册接收来自静态计时器滴答处理程序的计时器滴答事件,并且每次从X接收到事件并且某些条件为真时,Y递增计数器Int64。其他与该计数器相关的对象可以检查计数器的状态。只要对Y的某些引用存在而非X所持有的引用,定时器事件应该继续触发,并且X应该继续更新Y的计数器。一旦对Y的所有其他引用不再存在,Y的计数器就没用了,Y的更新工作也是一样。这将使X的通知无用,这将使X接收到的静态计时器事件无用。应该释放使用的计时器资源,并将X从静态处理程序中分离出来; X和Y应该不复存在。除了一个问题之外,所有这些都非常好,有序:即使X的有用性取决于Y的有用性,并且定时器的有用性取决于Y的有用性,但对象引用指向另一个方向。尽管可以安排一些事情,以便在放弃Y的情况下定时器将被释放,但这样做会在定时器正在使用时需要运行的代码中增加大量开销。

相关问题