C# 的 using 关键字的作用范围

5
我理解,每当我实例化一个实现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 关键字一起使用的大括号呢?


8
就像没有括号的 iffor 一样。 - Vladimir
此外:实际上,有一些边缘情况是不应该这样做的:https://dev59.com/6nRB5IYBdhLWcg3wn4QL - millimoose
你可以试一下。通过一些简单的实验,很容易弄清楚。 - Servy
@VladimirFrolov 哦,非常感谢。那很有道理。现在我感到很傻,没有意识到这一点。 - Ryan Ries
12个回答

8
几乎在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”语句没有正确缩进,这让我对作用域感到不确定。谢谢! - Ryan Ries
如果Visual Studio公平地处理嵌套的using语句,它会像缩进其他语句一样缩进每个语句。但那看起来会非常丑陋。它足够聪明,更喜欢风格和整洁。 - Trevor Elliott
我本能地喜欢这个,尽管经过反思,我想我还有一些额外的想法。 :-) - Craig Tullis

6
没有花括号的using语句仅在下一个语句的范围内生效 - 类似于if条件语句的工作方式。
using (SecureString s = new SecureString())
    s.Foo(); // Works
s.Foo(); // out of scope

作为个人偏好,我总是包含花括号,即使对于单语句的if/using结构,以避免像这样的混淆情况。

3

然后它只在使用语句之后的下一个命令中有效。

请注意,这并不意味着下一行,而是下一个命令


2

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

并不是花括号会自动添加,而是所有这些结构的主体始终只有一个语句;花括号允许您将多个语句视为单个语句。 如果您已经有一个语句,那么它们就不是必需的。 它不像语法特别将大括号列为可选项。 这些结构仅表示它们后面跟随一个语句,无论该语句是什么。 - Servy
没错,但我认为用我描述的方式更有意义,因为这是人们通常想到的方式。 - Tim S.

2

大家都已经回答了这个问题,你可以使用内联的using语句来达到与下面代码相同的效果:

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

然而,我认为您不应这样做,因为这会使代码变得难以阅读,并且会让其他开发人员感到困惑。事实上,您提出这个问题,已经证明了这一点。

2

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

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

使用 if 关键字而不带括号是相同的:

if(x == y)
    x = 4;

1

进一步补充其他答案:

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

// This is not.

如果你要给我点踩,至少有智慧评论一下为什么我的回答不值得赞同。否则我怎么能从我的“错误”中学习呢? - Jason Evans
如何进一步澄清代码意图的想法:http://stackoverflow.com/a/22675224/618649 - Craig Tullis

1

如果您不使用括号,则只有下一个语句是有效的。

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

如果你想包含多个语句,使用括号。

2
更准确地说,它是下一个语句 - 可能跨越多行,甚至在上一行。如果该行包含多个语句,则只有第一个语句位于using语句内。 - Jon Skeet
@JonSkeet 你说得完全正确。我已经更新了我的答案以便更加清晰明了。 - gleng

1
如果没有括号,你只会得到一行。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");

0

查看反汇编将会准确地展示正在发生的事情。

  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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接