【问题标题】:The scope of C#'s using keywordC# using 关键字的范围
【发布时间】:2013-10-26 02:17:33
【问题描述】:

我了解,每当我实例化一个实现 IDisposable 的类时,我都应该使用 using 关键字以确保它被正确处理。

像这样:

using (SecureString s = new SecureString())
{

}

以上内容对我来说很容易理解——我可以使用s,但是我想在那些括号内,但是一旦我离开这些括号,我就不能再引用s。范围很容易看到。

但我不明白的是,当您使用不带括号的 using 时它是如何工作的。

private void Function()
{
    // Some code here

    using (SecureString s = new SecureString())

    // more code here
}

您根本不需要使用括号...所以...如果using 没有括号,我怎么知道我可以在哪里使用该对象以及它在哪里被处理?关键字?

【问题讨论】:

  • 就像iffor 不带括号。
  • 旁白:实际上在某些极端情况下这是错误的做法:stackoverflow.com/questions/573872/…
  • 你可以试试看。通过一些简单的实验很容易弄清楚......
  • @VladimirFrolov 哦,谢谢。这完全有道理。我觉得现在没有意识到这一点很愚蠢。

标签: c# using-statement


【解决方案1】:

在 C# 中几乎所有情况下,当您可以选择使用大括号时,您都可以用一行代码替换它们。

考虑一个 if 语句:

if (someBool)
    DoSomething();
else
    DoSomethingElse();

这与以下内容一样有效:

if (someBool)
{
    // do lots of things
}
else
    DoSomethingElse();

这对于任何可以使用{} 括号的时间几乎都是正确的。

using 语句的好处是您可以像这样嵌套它们:

using (var stream = new NetworkStream(...))
using (var sslStream = new SslStream(stream))
using (var reader = new StreamReader(sslStream))
{
    reader.WriteLine(...);
}

这等价于:

using (var stream = new NetworkStream(...))
{
    using (var sslStream = new SslStream(stream))
    {
        using (var reader = new StreamReader(sslStream))
        {
            reader.WriteLine(...);
        }
    }
}

虽然我认为你会同意它看起来更好看。

【讨论】:

  • 哇,我的问题在 5 分钟内有 10-12 个正确答案。我不知道该选哪个答案。我现在问这么明显的问题感到很愚蠢。但是我之所以选择这个,是因为您准确地指出了导致我困惑的那种情况,多个嵌套的 using 语句没有正确地制表符/缩进,这让我对范围感到不确定。谢谢!
  • 如果 Visual Studio 公平地对待嵌套 using 语句,它会像缩进其他语句一样缩进每个语句。但这看起来真的很难看。它足够聪明,更喜欢风格和整洁。
【解决方案2】:

不带大括号的using 表示using 仅在下一条语句的范围内 - 与if 条件的工作方式相同。

using (SecureString s = new SecureString())
    s.Foo(); // Works
s.Foo(); // out of scope

作为个人喜好,我总是包含大括号,即使对于单个语句 if/using 构造,也可以避免这样的混乱情况。

【讨论】:

    【解决方案3】:

    那么只有在using语句之后的下一条命令中才有效。

    请注意,这并不意味着下一行。只有下一个命令

    【讨论】:

      【解决方案4】:

      范围基本上仅限于下一行。

      using(var s = new SecureString())
          s.Whatever();
      s.Whatever() // ERROR! s is out of scope
      

      这与使用不带括号的if关键字相同:

      if(x == y)
          x = 4;
      

      【讨论】:

        【解决方案5】:

        对于usingifforeach 之类的语句,如果不包含括号,则范围只是下一条语句(通常是下一行)。就像您在下一条语句周围包含括号一样对待它。例如

        if (someCondition)
            Console.WriteLine("someCondition is true!");
        Console.WriteLine("I run regardless");
        // same as
        if (someCondition)
        {
            Console.WriteLine("someCondition is true!");
        }
        
        using (var s = new SecureString())
            Console.WriteLine(s); // this works!
        //Console.WriteLine(s); // s is no longer in scope, this won't compile
        

        【讨论】:

        • 大括号不是自动加进去的;就是所有这些结构的主体始终是一个语句;花括号允许您将多个语句视为单个语句。如果您已经有一个声明,那么它们根本就不需要。它不像语法专门将大括号列为可选。这些结构只是说它们后面跟着一个语句,无论该语句是什么。
        • 没错,但我认为按照人们通常认为的方式来描述它更有意义。
        【解决方案6】:

        正如每个人都回答的那样,您基本上可以有一个与此等效的内联 using 语句:

        using(var s = new SecureString()) s.Whatever();
        

        但是请不要这样做,因为我认为这会降低代码的可读性并让其他开发人员感到困惑。我认为您提出这个问题的事实很好地说明了这一点。

        【讨论】:

          【解决方案7】:

          扩展其他答案:

          using (SecureString s = new SecureString())
              // This statement is in using scope.
          
          // This is not.
          

          【讨论】:

          • 如果您要投反对票,至少要聪明地评论为什么我的回答值得投反对票。否则我怎么能从我的“错误”答案中学习?
          • 关于如何阐明代码意图的进一步想法:stackoverflow.com/a/22675224/618649
          【解决方案8】:

          如果没有括号,你只会得到一行。与“if”或“for”语句相同。 例如:

          for (int i = 0; i < 10; i++)
              Console.WriteLine(i.ToString());
          
          if (str == "hello")
              Console.WriteLine("hello back!");
          
          using (SecureString s = new SecureString())
              Console.WriteLine("Here we have s");
          
          Console.WriteLine("but here it's out of scope already");
          

          【讨论】:

            【解决方案9】:

            如果不使用括号,则只有下一条语句有效

            using (SecureString s = new SecureString())
                // statement here
            

            如果要包含多个语句,请使用方括号。

            【讨论】:

            • 更准确地说,它是下一个语句——它可能跨越几行,甚至在上一行。如果该行包含多个语句,则只有第一个语句会在 using 语句中。
            • @JonSkeet 你说得对。为了清楚起见,我更新了我的答案。
            【解决方案10】:

            using,就像forwhileifelse 和许多其他关键字一样,始终只使用一个语句作为块的“主体”。

            如果程序员想要在主体中执行多个语句,他们可以使用花括号来创建一个复杂的语句,即执行多个(或零个)语句的单个语句。当您在 usingif 之后使用花括号时,您是在说“这里的所有内容都应该被视为一个语句”。如果您已经恰好以一个语句开头,则无需使用大括号将其转换为单个语句,因此您可以省略它们。

            【讨论】:

              【解决方案11】:

              查看反汇编将向您展示到底发生了什么。

                static private void Function()
                {
                   Console.WriteLine("before");
              
                   using (SecureString s = new SecureString())
                      s.Clear();
              
                   Console.WriteLine("after");
                }
              

              使用IL Dissassembler,上面的函数反汇编成下面的代码。您可以看到using 语句是用try-finally 包装语句的其余部分来实现的(这可能是一个由花括号定义的块,但在这种情况下只是分号)。您正在使用的对象位于finally 块中。然后,在finally 之外,代码继续执行源中的下一条语句。

              .method private hidebysig static void  Function() cil managed
              {
                // Code size       56 (0x38)
                .maxstack  2
                .locals init ([0] class [mscorlib]System.Security.SecureString s,
                         [1] bool CS$4$0000)
                IL_0000:  nop
                IL_0001:  ldstr      "before"
                IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
                IL_000b:  nop
                IL_000c:  newobj     instance void [mscorlib]System.Security.SecureString::.ctor()
                IL_0011:  stloc.0
                .try
                {
                  IL_0012:  ldloc.0
                  IL_0013:  callvirt   instance void [mscorlib]System.Security.SecureString::Clear()
                  IL_0018:  nop
                  IL_0019:  leave.s    IL_002b
                }  // end .try
                finally
                {
                  IL_001b:  ldloc.0
                  IL_001c:  ldnull
                  IL_001d:  ceq
                  IL_001f:  stloc.1
                  IL_0020:  ldloc.1
                  IL_0021:  brtrue.s   IL_002a
                  IL_0023:  ldloc.0
                  IL_0024:  callvirt   instance void [mscorlib]System.IDisposable::Dispose()
                  IL_0029:  nop
                  IL_002a:  endfinally
                }  // end handler
                IL_002b:  nop
                IL_002c:  ldstr      "after"
                IL_0031:  call       void [mscorlib]System.Console::WriteLine(string)
                IL_0036:  nop
                IL_0037:  ret
              } // end of method Program::Function
              

              【讨论】:

                【解决方案12】:

                正如其他cmets中提到的,当你提到的方式使用using语句时,只有当你有一行时。

                我想在C# 8.0 中添加您可以这样使用"using declarations"

                private void Function()
                {
                    // Some code here
                
                    using SecureString s = new SecureString();
                
                    // more code here
                }
                

                这样,using 语句的生命周期将延长到声明它的范围的末尾。然后,using 本地变量将按照声明它们的相反顺序进行处理。

                【讨论】:

                  猜你喜欢
                  • 2012-09-13
                  • 1970-01-01
                  • 2013-05-30
                  • 1970-01-01
                  • 2010-10-07
                  • 2011-01-31
                  • 1970-01-01
                  • 2010-12-18
                  • 1970-01-01
                  相关资源
                  最近更新 更多