在IDisposable对象上使用语句 - 调用Dispose方法的延迟

时间:2010-10-30 21:06:29

标签: c# idisposable using-statement

正如描述这个article,关于在IDisposable对象上使用的用法,它说了一个有趣的词:

...使用块,在块结束后的某个时间自动调用Dispose方法。 (可能不是立即的;它取决于CLR。)

这里有趣的是“可能不是立即的;它取决于CLR ”。 任何人都可以提供更多细节吗?因为我们有一些奇怪的情况,似乎在代码上使用(new MyDisposable()){...},在块结束后} NOT 立即调用MyDisposable实例上的Dispose方法,但是有些时间过了。

更新:结论对我来说,在我看来,我在别处有问题。我认为可以在使用块结束后的某个时间调用Dispose方法。但是当它不是那样的时候,我必须在我的代码中的其他地方找到问题。 谢谢你的回复!

4 个答案:

答案 0 :(得分:9)

using (SomeDisposableResource resource = new SomeDisposableResource())
{
    // TODO: use the resource
}

相当于:

SomeDisposableResource resource = new SomeDisposableResource();
try
{
    // TODO: use the resource
}
finally
{
    if (resource != null)
    {
        ((IDisposable)resource).Dispose();
    }
}

所以,由你来得出结论。一切都取决于你如何定义立即。在多线程环境中,可以在try块和资源处理之间执行其他操作,但是当它包含在finally块中时,可以保证调用Dispose方法

答案 1 :(得分:6)

我对这种说法持怀疑态度,并认为它们意味着其他东西(可能是垃圾收集)。 using语句只是try / finally块的语法糖,finally块调用dispose。鉴于此C#:

using (var fs = new FileStream("C:\\blah.txt", FileMode.CreateNew))
{
    fs.WriteByte(7);
}

IL看起来像这样:

//snipped
L_000e: nop 
L_000f: ldstr "C:\\blah.txt"
L_0014: ldc.i4.1 
L_0015: newobj instance void [mscorlib]System.IO.FileStream::.ctor(string, valuetype [mscorlib]System.IO.FileMode)
L_001a: stloc.0 
L_001b: nop 
L_001c: ldloc.0 
L_001d: ldc.i4.7 
L_001e: callvirt instance void [mscorlib]System.IO.Stream::WriteByte(uint8)
L_0023: nop 
L_0024: nop 
L_0025: leave.s L_0037
L_0027: ldloc.0 
L_0028: ldnull 
L_0029: ceq 
L_002b: stloc.1 
L_002c: ldloc.1 
L_002d: brtrue.s L_0036
L_002f: ldloc.0 
L_0030: callvirt instance void [mscorlib]System.IDisposable::Dispose()
L_0035: nop 
L_0036: endfinally 
L_0037: nop 
L_0038: nop 
L_0039: ret 
.try L_001b to L_0027 finally handler L_0027 to L_0037

注意最后一行它只是一个.try和.finally。这也在C#规范中指出:http://msdn.microsoft.com/en-us/library/aa664736.aspx

答案 2 :(得分:1)

奇怪......很奇怪......

这篇文章可能错了。 using语句编译为完全

MyDisposableObject obj = new MyDisposableObject()
try
{
    obj.Use();
}
finally
{
    if (obj!=null) obj.Dispose();
}

Dispose方法在finally块中显式调用,与析构函数/ Finalize方法不同,后者在收集之前调用,但由GC自行决定。

我认为这篇文章中的错误。最多...... 某个时候可能指的是线程调度。如果CLR决定计划其他线程一旦命中finally,那么你可能会在100%加载的CPU和运行更高优先级的任务上等待一些非常少的时间来运行Dispose。

Dispose要同步的重要!想想关于托管资源的这个例子

public void Log(string message)
{
    using(StreamWriter sw = new StreamWriter(File.Append(path)))
    {
        sw.WriteLine(message);
    }
}
public static void Main()
{
    Log("Hello");
    Log("World");
}

关于流和文件的Dispose调用实际上会关闭它们。如果文章中的内容是真的,那么您将使用打开的文件调用第二个Log,从而立即导致IOException

答案 3 :(得分:0)

我不知道它来自哪里,这与我在using语句中看到的所有其他内容相矛盾,例如this article表示这样的using块:

using (Font font1 = new Font("Arial", 10.0f)) {
  byte charset = font1.GdiCharSet;
}

实现如下:

Font font1 = new Font("Arial", 10.0f);
try {
    byte charset = font1.GdiCharSet;
} finally {
  if (font1 != null) {
    ((IDisposable)font1).Dispose();
  }
}

因此,Dispose方法将在using块之后的下一个语句之前调用。

撰写文章的人可能会将对象在不再使用后的某个时间与垃圾收集的方式混淆,或者未处置的一次性对象如何通过后台线程调用其终结器。