为什么闭包对于使用声明的 C# 8.0 中的变量不同?

Why are closures different for variables from C# 8.0 using declarations?

我注意到 C# 8.0 编译器为捕获的 IDisposable 变量构建闭包 classes 的方式有所不同,这些变量是使用 C# 8.0 using 声明声明的,而不是使用声明的变量classic using 语句。

考虑这个简单的问题 class:

public class DisposableClass : IDisposable
{
    public void Dispose() { }
}

以及这个示例代码:

public void Test()
{
    using var disposable1 = new DisposableClass();
    using var disposable2 = new DisposableClass();

    Action action = () => Console.Write($"{disposable1}{disposable2}");
}

编译器生成这段代码:

[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
    public DisposableClass disposable1;

    public DisposableClass disposable2;

    internal void <Test>b__0()
    {
        Console.Write(string.Format("{0}{1}", disposable1, disposable2));
    }
}

public void Test()
{
    <>c__DisplayClass0_0 <>c__DisplayClass0_ = new <>c__DisplayClass0_0();
    <>c__DisplayClass0_.disposable1 = new DisposableClass();
    try
    {
        <>c__DisplayClass0_.disposable2 = new DisposableClass();
        try
        {
            Action action = new Action(<>c__DisplayClass0_.<Test>b__0);
        }
        finally
        {
            if (<>c__DisplayClass0_.disposable2 != null)
            {
                ((IDisposable)<>c__DisplayClass0_.disposable2).Dispose();
            }
        }
    }
    finally
    {
        if (<>c__DisplayClass0_.disposable1 != null)
        {
            ((IDisposable)<>c__DisplayClass0_.disposable1).Dispose();
        }
    }
}

这看起来完全没问题。但后来我注意到,如果我用 using 语句声明这两个变量,则闭包 class 的生成方式会大不相同。这是示例代码:

public void Test()
{
    using (var disposable1 = new DisposableClass())
    using (var disposable2 = new DisposableClass())
    {
        Action action = () => Console.Write($"{disposable1}{disposable2}");
    }
}

这就是我得到的:

[CompilerGenerated]
private sealed class <>c__DisplayClass0_0
{
    public DisposableClass disposable1;
}

[CompilerGenerated]
private sealed class <>c__DisplayClass0_1
{
    public DisposableClass disposable2;

    public <>c__DisplayClass0_0 CS$<>8__locals1;

    internal void <Test>b__0()
    {
        Console.Write(string.Format("{0}{1}", CS$<>8__locals1.disposable1, disposable2));
    }
}

为什么会这样?其余代码看起来相同,我认为 using 声明应该 与 using 语句完全相同,后者被视为块它在其中声明的当前块。

更不用说为使用声明生成闭包 class 的方式看起来更清晰,最重要的是, 通过反射更容易探索。

如果有人知道为什么会发生这种情况,我希望得到一些见解。

谢谢!

编译器正在为每个 "scope"...生成一个 [CompilerGenerated] class... 在第一个示例中,只有一个作用域,即整个 Test() 方法。在第二个例子中(你没有给出),有两个范围,两个 using.

第二个例子的代码大概是:

public void Test()
{
    using (var disposable1 = new DisposableClass())
    {
        using (var disposable2 = new DisposableClass())
        {
            Action action = () => Console.Write($"{disposable1}{disposable2}");
        }
    }
}

正如 juharr 所指出的,这两个代码块产生相同的代码:

using (DisposableClass disposable1 = new DisposableClass(), disposable2 = new DisposableClass())
{
    Action action = () => Console.Write($"{disposable1}{disposable2}");
}

using var disposable1 = new DisposableClass();
using var disposable2 = new DisposableClass();

Action action = () => Console.Write($"{disposable1}{disposable2}");