我一直认为答案是否定的,但我找不到任何来源说明这一点。
在下面的课程中,我是否可以在终结器中访问C
实例的(托管)字段/属性,即ReleaseUnmanaged()
?有什么限制,如果有的话? GC或终结是否会将这些成员设置为空?
我唯一能找到的是终结队列中的东西可以按任意顺序完成。因此,在这种情况下,由于recommendation类型应该允许用户多次调用Dispose()
,为什么建议的模式会使用disposing
boolean
?如果我的终结器调用Dispose(true)
而不是Dispose(false)
?
public class C : IDisposable
{
private void ReleaseUnmanaged() { }
private void ReleaseOtherDisposables() { }
protected virtual void Dispose(bool disposing)
{
ReleaseUnmanaged();
if (disposing)
{
ReleaseOtherDisposables();
}
}
~ C()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
答案 0 :(得分:4)
一般情况下 - 是的,你可以。如果一个类有非空的终结器,第一次GC会收集这个类的实例,它会调用终结器(只有你之前没有调用GC.SuppressFinalize
)。从终结器看到的对象实例看起来就像上次触摸它时一样。您甚至可以创建从根到您的实例的新(直接或间接)链接,从而resurrect它。
即使你持有非托管对象的非托管指针并检查原始内存内容,你也不应该看到部分解除分配的对象,因为.NET使用复制GC。如果实例在收集期间处于活动状态,则会将其提升为下一代,或者与其他实例一起移动到全新的内存块。如果无法访问,则将其保留在原来的位置,或者释放整个堆并返回到OS。但请记住,终结器可以并且将在未能构造的对象的实例上调用(即,在对象构造期间抛出异常时)。
编辑:对于编写良好的课程中的Dispose(true)
vs Dispose(false)
,从长远来看,应该没有多大区别。如果您的终结器名为Dispose(true)
,它只会删除您的对象到其他对象的链接,但由于您的对象已无法访问,因此释放对象引用的其他实例与其可访问性无关。
有关.NET GC实现详细信息的更多详细信息,我建议Joseph和Ben Albahari C# 5.0 in a Nutshell。
答案 1 :(得分:3)
如果有任何代码可以通过任何方式获取对它的引用,GC将永远不会收集任何对象。如果对象中存在的唯一引用是弱引用,则GC将使它们无效,以便任何代码都无法获得对该对象的引用,于是它将能够收集它。
如果一个对象有一个活动的终结器,那么如果GC 将收集它(但是对于终结器的存在),GC将把它添加到一个对象队列,其终结器应该是尽快运行,并且已经这样做,取消激活它。队列中的引用将阻止GC收集对象,直到终结器运行;一旦终结器完成,如果该对象没有其他引用并且它没有重新注册其终结器,它将不再存在。
终结器访问外部对象的最大问题是:
终结器将在线程上下文中运行,该上下文与使用该对象的任何线程上下文无关,但不应执行任何无法保证快速完成的操作。这通常会产生矛盾的要求,即代码在访问其他对象时会使用锁定,但代码在等待锁定时不会阻塞。
如果终结者有另一个也有终结者的对象的引用,则无法保证哪个对象会先运行。
这两个因素严重限制了具有终结器的对象的能力,以便能够访问他们不拥有的外部对象。此外,由于实现了最终化的方式,系统可以在没有强引用时决定运行终结器,但是在终结器运行之前创建和使用外部强引用;终结者的对象在是否发生这种情况时没有发言权。