我希望我能正确地了解我的事实,如果没有请纠正我。
1)您使用Dispose来清理非托管资源,这意味着不会为它们进行垃圾回收。
2)值类型存储在堆栈中,引用类型存储在堆上,引用类型的指针存储在堆栈中(不是很确定这个,但我认为它是正确的)
3)垃圾收集器调用增强器。
4)垃圾收集由CLR调用,而不是由用户(尽管他可以)调用引用类型,但是当它们超出范围时,值类型被销毁(抱歉找不到更好的单词)
我知道using语句和我的工作方式,但我认为如果CLR会为用户调用Dispose会更容易。
所以我的问题是:“如果引用类型的指针存储在堆栈中的编译器和CLR,就知道对象何时超出范围。为什么编译器不会生成一些调用Dispose的IL代码,或者为什么CLR不像他们对Finilizers和Destructors那样做呢“。
很抱歉,如果我的问题不明确,我很乐意用您需要的任何其他信息进行更新。 谢谢。
答案 0 :(得分:4)
问题在于:
如果引用类型的指针存储在堆栈编译器和CLR上,则知道对象何时超出范围
这太过于简单化了。您可以拥有属于一次性类型的私有字段。这些不会存储在堆栈中,除了通过使用垃圾收集之外,CLR无法知道对象何时不再使用。当然,垃圾收集已经提供了一种机制 - 终结器 - 通过它可以处理非托管资源。
答案 1 :(得分:2)
可以编写类,因此只有终结器才能清除非托管资源,而根本不使用Dispose
。在这种情况下,GC会在不再使用后“在某个时刻”清理这些资源。
但是,在某些情况下,将这些资源保留到将来的任意点是一个问题。例如,如果您打开系统文件句柄,其他应用程序可能无法打开该文件,直到句柄关闭。通过允许应用程序代码创建和调用Dispose
方法,应用程序可以确定性地清理非托管资源,确保系统不会进入等待GC“某个时间”运行的状态
答案 2 :(得分:0)
给出代码
void Blah(Thing Boz)
{
DisposableThing Foo = new DisposableThing();
Boz.SomeMethod(Foo);
}
编译器在确定退出方法后将不再需要引用Foo
。如果在那时,宇宙中的任何位置都没有其他引用到该引用所标识的DisposableThing
,那么DisposableThing
应该在它被放弃之前调用其Dispose
方法。问题是编译器无法知道SomeMethod
是否可能在其他地方存储了对该对象的引用。
在绝大多数创建IDisposable
对象的情况下,它将被传递给编译器一无所知的方法。因此,编译器无法知道对象的特定引用是否在超出范围时是对象的唯一引用。
如果方法写成:
void Blah(Thing Boz)
{
using (DisposableThing Foo = new DisposableThing())
{
Boz.SomeMethod(Foo);
}
}
指示编译器在Dispose
返回后对新创建的对象调用Boz.SomeMethod()
。编译器不会试图确定Boz.SomeMethod()
是否可以保留引用 - using
语句无论如何都会调用Dispose
。