C# 8 使用声明范围混淆

C# 8 Using Declaration Scope Confusion

使用新的 C# 8 Using 声明语法,第二个连续 using 语句的包含范围是什么?

TL;DR

在 C# 8 之前,有一个连续的 using 语句,如:

using(var disposable = new MemoryStream())
{
    using(var secondDisposable = new StreamWriter(disposable))
    {}
}

将扩展为如下内容 (My Source):

MemoryStream disposable = new MemoryStream();
try {
    {
        StreamWriter secondDisposable = new StreamWriter(disposable);    
        try{
            {}
        }
        finally {
            if(secondDisposable != null) ((IDisposable)secondDisposable).Dispose();
        }
    }
}
finally {
    if(disposable != null) ((IDisposable)disposable).Dispose();
}

我知道还有另外两种可能的扩展,但它们大致都是这样

升级到 C# 8 后,Visual studio 提供了一个代码清理建议,我不确定我认为它是等效的建议。

将上面连续的using语句转换为:

using var disposable = new MemoryStream();
using var secondDisposable = new StreamWriter(disposable);

对我来说,这会将第二个的范围更改为与第一个相同的范围。在这种情况下,它可能会巧合地以正确的顺序处理流,但我不确定我是否喜欢依赖这种巧合。

为了清楚 VS 要求我做什么:我首先转换了内部(这是有道理的,因为内部仍然包含在外部的范围内)。然后我转换了外部(这在本地有意义,因为它仍然包含在方法的范围内)。这两个清理的组合是我很好奇的。

我也认识到我对此的想法可能略有(甚至是显着)偏差,但根据我今天的理解,这似乎不正确。我的评估中缺少什么?我错了吗?

我唯一能想到的是,在声明语句之后的所有内容的扩展中插入了某种隐式作用域。

In this case, It would probably coincidentally dispose of the streams in the correct order, but I'm not certain I like to rely on that happy coincidence.

来自spec proposal

The using locals will then be disposed in the reverse order in which they are declared.

所以,是的,他们已经考虑过了,并按照预期的顺序进行处理,就像之前的链接 using 语句一样。

我想看看使用它的真实功能。编译器不会随意更改分配或处置的范围或顺序。如果你有这样的方法:

void foo()
{
    using(var ms = new MemoryStream())
    {
        using(var ms2 = new MemoryStream())
        {
            /// do something
        }
    }
}

然后 Dispose() 顺序无关紧要,因此编译器可以安全地安排它认为合适的事情。在其他情况下,顺序 很重要,编译器应该足够聪明以识别这一点。我不会在 "coincidence" 下提交 "good AST analysis."

说明Daminen的答案;当你有类似的方法时;

public void M() 
{
    using var f1 = new System.IO.MemoryStream(null,true);    
    using var f2 = new System.IO.MemoryStream(null,true);
    using var f3 = new System.IO.MemoryStream(null,true);
}

IL将其转换为;

public void M()
{
    MemoryStream memoryStream = new MemoryStream(null, true);
    try
    {
        MemoryStream memoryStream2 = new MemoryStream(null, true);
        try
        {
            MemoryStream memoryStream3 = new MemoryStream(null, true);
            try
            {
            }
            finally
            {
                if (memoryStream3 != null)
                {
                    ((IDisposable)memoryStream3).Dispose();
                }
            }
        }
        finally
        {
            if (memoryStream2 != null)
            {
                ((IDisposable)memoryStream2).Dispose();
            }
        }
    }
    finally
    {
        if (memoryStream != null)
        {
            ((IDisposable)memoryStream).Dispose();
        }
    }
}

与嵌套using语句相同 你可以从这里查看:https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQMwAJboMLoN7LpGYZQAs6AsgBQCU+hxTUADOgG4CGATugGZx0AXnQA7AKYB3THAB0ASQDysyuIC2Ae24BPAMoAXbuM5rqogK4AbSwBpD58bQDcTRkyKsOPfjGFipMgrKqpo6BkYmZla29o5Obu6eXLx8GCIS0lBySirqWnqGxqYW1nbcDs4JAL7IVUA===