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

C# 在IDisposable对象上使用语句-调用Dispose方法的延迟,c#,idisposable,using-statement,C#,Idisposable,Using Statement,在描述这一点时,关于在IDisposable对象上使用的用法,它说了一个有趣的词: …使用块,在块结束后的某个时间自动调用Dispose方法。(它可能不是即时的;它取决于CLR。) 这里有趣的是“它可能不是即时的;它取决于CLR”。 有人能提供更多的细节吗?因为我们遇到了一些奇怪的情况,在使用(new MyDisposable()){…}的代码上,在块的结尾}之后,它不会立即调用mydispose实例上的Dispose方法,而是在一段时间之后 更新:结论对我来说,似乎我在其他地方有问题。我认为D

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

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

这里有趣的是“它可能不是即时的;它取决于CLR”。 有人能提供更多的细节吗?因为我们遇到了一些奇怪的情况,在使用(new MyDisposable()){…}的代码上,在块的结尾}之后,它不会立即调用mydispose实例上的Dispose方法,而是在一段时间之后

更新:结论对我来说,似乎我在其他地方有问题。我认为Dispose方法可以在使用block ends之后调用。但当情况并非如此时,我必须在代码的其他地方找到问题。 感谢您的回复

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
块中,因此可以保证调用该方法。

奇怪。。。很奇怪

这篇文章可能是错的。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

我不知道这是从哪里来的,这与我所看到的关于
使用
语句的所有其他内容相矛盾,例如,它说一个
使用
块如下:

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();
  }
}
//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
实现方式如下:

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();
  }
}
//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
因此,
Dispose
方法将在
using
块后面的下一条语句之前调用


写这篇文章的人可能会把它与一个对象在不再使用后如何进行垃圾收集,或者未处理的一次性对象如何通过后台线程调用其终结器相混淆。

我对这一说法有点怀疑,并认为它们是指其他东西(可能是垃圾收集)。using语句只是try/finally块的语法糖,finally块在其中调用dispose。鉴于此C#:

IL如下所示:

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();
  }
}
//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#spec中指出。

理论与事实的结合使用(没有pan意图)。NET 5和xunit

当Try..Catch..Finally构造在最终断言之前处理对象时,Using块不会

public class DisposableUnitTests
{

    private class DisposableObject : IDisposable
    {

        public bool Disposed { get; set; }

        protected virtual void Dispose( bool disposing )
        {
            if ( disposing )
                this.Disposed = true;
        }

        public void Dispose()
        {
            this.Dispose( true );
        }
    }

    [Fact]
    public void CanTryCatchFinallyDispose()
    {
        DisposableObject Target = new DisposableObject();
        try
        {
            Assert.False( Target.Disposed, $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
        }
        catch ( Exception )
        {

            throw;
        }
        finally
        {
            ((IDisposable)Target)?.Dispose();
        }
        Assert.True( Target.Disposed , $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
    }

    [Fact]
    public void CanUsingBlockDispose()
    {
        using DisposableObject Target = new DisposableObject();
        {
            Assert.False( Target.Disposed, $"{nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
        }
        Assert.False( Target.Disposed, $"Using block not disposing {nameof( DisposableObject )}.{nameof( DisposableObject.Disposed )}" );
    }

}

我不知道,但考虑到using块“确切”编译成什么是错误的,这篇文章值得怀疑。它们甚至不包括try/finally块。我当时停止阅读。退出using块的范围时立即调用Dispose,如果可能的话,它是晚些时候调用的最终程序。这篇文章只是wro参考:该网页的作者对真实情况一无所知。引用:“Dispose、Finalize和析构函数都是相关的,但这超出了我们在这里讨论的范围”.是的,这是他没有得到的部分。不幸的是,任何人都可以在我们的互联网站上发布不正确的运球。你必须对任何单一的信息来源更加挑剔。我对这篇文章持批评态度,这就是我为什么在这里写这个问题的原因。我的问题是,我认为这种奇怪的延迟执行Dispose的情况就是我的情况。BuTo带着这些答案,我似乎在某个地方有问题。很酷,我不知道-因为海报可以在他的处置方法中设置断点,而在使用块结束后在另一行上设置断点,并且不应该有任何疑问,这就是你所暗示的吗?请注意,<代码>资源< /代码>这里是考虑。using构造中的ed只读。如果生成对象的表达式
=new SomeDisposableResource(),则
finally
块中的null测试生效
在本例中,调用了一个可以返回null的方法。还有一些关于结构的详细信息在这种情况下并不重要。简言之,调用
Dispose
是作为using构造的
}
的一部分。是的,我的意思是……如果取出原始的
}
并用finall替换它答案的y部分,你可以认为它是“作为
}
的一部分”。这是C#code如何执行的一个心理模型,但是是的,我知道,IL:)中没有大括号。让我们在调用dispose之前做一个空检查,这就是为什么这个构造对于处理以前创建的对象也很有用,如果不是空的话:
使用(x){
-比在(x!=null)x.Dispose()时必须写入
要好得多到处都是,虽然不是很清楚。是的,我忘了。实际上,它甚至应该强制转换为IDisposable,否则代码将无法与显式实现IDisposable(如流)的类一起工作,但这仍然是意义所在;)不过,它并不总是强制转换为IDisposable,请在实现IDisposable的结构上使用
查看IL for