【问题标题】:C# 8 Using Declaration Scope ConfusionC# 8 使用声明范围混淆
【发布时间】:2020-04-10 04:36:11
【问题描述】:

使用新的 C# 8 使用声明语法,第二个连续 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 要求我做什么:我首先转换了内部(这是有道理的,因为内部仍然包含在外部的范围内)。然后我转换了外部(这在本地是有意义的,因为它仍然包含在方法的范围内)。这两个清理的组合是我很好奇的。

我也认识到我对此的想法可能会略微(甚至是戏剧性地)偏离,但据我今天的理解,这似乎并不正确。我的评估中缺少什么?我不在基地吗?

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

【问题讨论】:

  • 这是一个小问题,但值得一提。在 C# 中,作用域被定义为程序文本的区域,在该区域中,事物可以通过其非限定的简单名称来引用。您使用范围来表示“局部变量的生命周期”。这两件事之间存在联系,因为局部变量声明空间这些变量的名称范围是程序文本的同一区域。但请记住,C# 允许延长缩短局部变量的生命周期,以使该生命周期不同于控制在范围。
  • 我的观点是,将“使用”视为“当控制离开变量范围时处理”可能会令人困惑。正如您所注意到的,“使用”实际上是一个try-finally,它在控制进入finally 时释放。同样,这与 变量 的生命周期独立,该变量持有对正在处置的资源的引用。该变量的生命周期可以延长!
  • @EricLippert “C# 8 中的新功能”文档 here 将 using 声明描述为“...告诉编译器所声明的变量应放置在封闭范围的末尾” .这和你的观点有冲突吗?
  • 啊,在查看我的 cmets 时,我发现我无意中通过尝试更精确而使事情变得不必要地更加混乱;我的错。对Dispose 的调用将在控制离开范围时发生,即使变量的生命周期被延长,这就是我应该强调的。您在问题中表达的担忧是关于处置发生的时间,而不是变量的生命周期。抱歉,如果这令人困惑。
  • 好吧,我现在意识到我在第四段中埋下了头,因为我担心的是Dispose 而不是范围本身。

标签: c# visual-studio visual-studio-2019 c#-8.0


【解决方案1】:

在这种情况下,它可能会巧合地以正确的顺序处理流,但我不确定我是否喜欢依赖这个快乐的巧合。

来自spec proposal

然后,using 本地变量将按照声明它们的相反顺序进行处理。

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

【讨论】:

    【解决方案2】:

    说明达米宁的回答;当您有类似的方法时;

    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===

    【讨论】:

      【解决方案3】:

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

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

      那么Dispose() 的顺序无关紧要,所以编译器可以安全地安排它认为合适的东西。在其他情况下,顺序 很重要,编译器应该足够聪明以识别这一点。我不会把它归为“巧合”而是“良好的 AST 分析”。

      【讨论】:

      • 更新了问题以反映更合适的示例。
      猜你喜欢
      • 2022-01-13
      • 2016-10-22
      • 2011-12-22
      • 2010-11-24
      • 1970-01-01
      • 2023-03-12
      • 2020-02-16
      • 2011-08-06
      • 1970-01-01
      相关资源
      最近更新 更多