编译器(例如C#)可以在对象设置为null时自动生成对对象的Dispose方法的调用(当然,对象应该首先支持Dispose方法)。例如,如果我们写
cnSqlConnection = null;
和cnSqlConnection是一个SqlConnection类型的实例,可以在更新引用null之前C#编译器注入Dispose方法调用吗?
此外,由于框架类确实支持多次调用Dispose方法的情况,因此如果调用重复,则没有任何危害。
答案 0 :(得分:7)
(a)正确实现的对象应在其终结器中执行相同的清理逻辑。如果省略对Dispose
的调用,则终结器逻辑可能仍会运行。
(b)Raymond Chen explains the complexity of auto-dispose here。总结:让人类程序员在正确的位置调用Dispose
是非常安全的。如果你将自动处理结果合理化,那么你最终会得到引用计数,这就是CLR内存模型要避免的内容。
答案 1 :(得分:4)
这是非常不一致的,我举一些例子:
鉴于课程:
class A : IDisposable { public void Dispose() { } }
示例1:
IDisposable a = new A();
IDisposable b = a;
a = null; // The object is still alive in b, should it really be disposed?
示例2:
IDisposable a = new A();
IDisposable b = new A();
a = b; // a is not accessible anymore, but not set to null,
//shouldn't it be disposed here?
示例3:
private void Foo()
{
IDisposable a = new A();
// a is not used any more, but not set to null,
//shouldn't it be disposed here?
}
在c#中,using
块可以解决您前往的问题:
using (IDisposable a = new A())
{
// Do stuff
} // Here a.Dispose() is automatically called.
答案 2 :(得分:2)
Techincally,是的,编译器可以执行此操作,但理想情况下,不会。
原因是您在确定是否还有其他内容存在问题时会遇到问题。编译器无法推断谁在编译时持有引用(可能通过静态分析,但仍然无法保证)。
现在,有可能在运行时执行此操作,但这仍然不理想。每次将引用设置为null(标记和扫描)时,它都需要等效的GC。然后对于任何GCed,如果有一个IDisposable实现,请调用Dispose。这会将CLR拖入泥浆中,并使其表现得非常糟糕。
当然,总会有引用计数(在他对这个问题的评论中提到的nonnb),但这只是回到COM,而且也不理想。引用计数的复杂性首先催生了CLR的GC方面。
需要考虑的事项:如果您遇到的情况是您不知道谁实际拥有您的IDisposable实现,那么它代表了您系统中的设计缺陷。如果你有一个适用于这种实例的函数,它应该明确声明它将处理这样的实例,或者它将由调用者自行决定(后者是首选方法)。
答案 3 :(得分:2)
它不能,它只是一个对象引用。可能存在对同一对象的许多引用,如果编译器自动执行此操作,它们都将指向死对象。这是一个保证kaboom。
垃圾收集器的核心属性是编译器和您都无法找出对象存在多少引用。只有垃圾收集器才能做到这一点。它已经完成,如果所有引用都消失,它会自动完成一个对象。
IDisposable()的目的是不要等到这种情况发生。由于编译器无法执行此操作,也无法实现自动化,因此您需要使 promise 不存在对该对象的其他实时引用。如果您无法做出承诺,那么您不应该调用Dispose()并将其留给收集器。例如,COM互操作代码中的标准。
其他自动内存管理方案可以做到这一点。他们使用引用计数,在垃圾收集器成为主流之前很常见。昂贵且不可靠,因为它无法处理周期。微软资助了一项研究项目,为CLR添加引用计数。那是a big bust。之后GC得到了更多尊重。
答案 4 :(得分:1)
是的,编译器可以自动生成Dispose
调用。真正的问题是这是否是一个好主意。而这个问题的答案肯定是否定的。
请考虑以下示例。
void DoSomething(IDisposable disposable)
{
DoSomethingElse(disposable);
disposable = null;
}
在上面的示例中,DoSomethingElse
如何不对disposable
实例单独引用或DoSomething
的调用者做类似的事情?如果对象被处理掉而其他代码片段假定对象是“实时”的话,那真的会搞砸了。
一个更有趣的问题是IDisposable
具有Dispose
的结构在其范围结束时自动调用的原因。显而易见的是,为什么对参考类型这样做不起作用,但结构呢?考虑以下并发症。
我确信这份清单并非详尽无遗,但你明白了。这些问题是可以解决的,但是真的值得努力吗?编译器可能必须对它们施加如此多的限制,以至于它们的用例变得非常有限。这是一个有趣的主题。
关于这个主题,您可能会发现blog有趣。
答案 5 :(得分:0)
public static class MissingCompilerFeatures
{
public static void SetToNullAndDispose(ref IDisposable obj)
{
if (obj != null) { obj.Dispose(); obj = null; }
}
}
答案 6 :(得分:0)
将对象引用设置为null会将对象标记为已释放以进行垃圾回收,但不会显式调用Dispose。它如果一个对象实现了IDisposable,你应该在完成对象时显式地调用Dispose(),或者将它包装在using
语句中。
以下是关于IDisposable和垃圾收集的一些很好的信息: