当对象设置为null时,针对CLR的编译器是否可以生成Dispose方法调用?

时间:2010-08-23 14:57:08

标签: c# compiler-construction dispose

编译器(例如C#)可以在对象设置为null时自动生成对对象的Dispose方法的调用(当然,对象应该首先支持Dispose方法)。例如,如果我们写

cnSqlConnection = null;

和cnSqlConnection是一个SqlConnection类型的实例,可以在更新引用null之前C#编译器注入Dispose方法调用吗?

此外,由于框架类确实支持多次调用Dispose方法的情况,因此如果调用重复,则没有任何危害。

7 个答案:

答案 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和垃圾收集的一些很好的信息:

http://www.xtremedotnettalk.com/showthread.php?t=97910