C#的using语句是否可以不使用花括号来编写?

55

今天我在浏览一位同事的C#代码时发现了以下内容:

    using (MemoryStream data1 = new MemoryStream())
    using (MemoryStream data2 = new MemoryStream())
    {
        // Lots of code..........
     }

我一直看到using语句后面跟着一对花括号来定义对象生命周期的作用域。我的同事写的代码中,他说data1using语句中的花括号是不需要的,即使嵌套了data2using语句也可以达到相同的效果。那么,当省略花括号时会发生什么?


3
我的看法是,虽然像回答里展示的那样“可以”这么做,但出于可读性考虑,我认为不应该这么做。对我而言,这就像将 if/else/while/lock 等语句块用括号括起来一样,即使它们不是必需的,也更容易阅读。 - Joe Enos
15
在我看来,像上面那样使用堆叠的using语句比嵌套的using语句更容易阅读。特别是在需要链接 3-4 个流(Stream)/流读取器(StreamReader)执行一组操作的情况下。 - Joel Mueller
4
@Joel:也许像其他任何事情一样,应该考虑具体的情况。如果只有两个,我的意见是一定要用括号嵌套它们。如果像你说的有4个,也许堆叠式是更好的方法。但第一次在data2创建之前需要访问data1时,这意味着改变代码的可读性而不仅仅是添加一行代码。 - Joe Enos
@Joe:说得对。根据我的经验,我使用using块的情况通常是非常简单明了的。我从来没有遇到过在两个堆叠的using语句之间插入一些逻辑的情况,并且一旦编写完成,我很少需要修改它们。 - Joel Mueller
我是“不使用大括号”或“完全使用C#8的方式而不使用大括号”的团队成员,但另一个想法可能是将所有嵌套的using语句简化并将内部代码外包到一个方法中。这样很容易阅读。using (var data1 ...) { using (var data2 ...) { using (var data3 ...) { MethodCall(data1, data2, data3); } } } - ecth
8个回答

111

是的,你也可以把它们放在一个 using 语句中:

using (MemoryStream data1 = new MemoryStream(), 
                    data2 = new MemoryStream())
{
    // do stuff
}

28
请注意,只有当多个对象都属于相同类型时,才能将它们放在一个using语句中,本例中是MemoryStream - LukeH
1
@fearofawhackplanet:根据C#规范,行为没有任何区别。 - LukeH
1
@fletcher:它们的释放顺序与标准嵌套的“using”语句完全相同。 - LukeH
5
从 C# 规范中:「形如 using (ResourceType r1 = e1, r2 = e2, ..., rN = eN) statementusing 语句,等价于嵌套的一系列 using 语句:using (ResourceType r1 = e1) using (ResourceType r2 = e2) ... using (ResourceType rN = eN) statement」。 - LukeH
从C# 8.0开始,您根本不需要任何括号。只需使用“using声明”即可(请参见:https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8#using-declarations)。 - Dejan
显示剩余3条评论

23

6
刚刚被这种新的语法搞糊涂了:我有一段代码块,其中有一个新的 "using" 语法,我将其重构为一个返回可释放对象的新方法。当然,这导致了运行时错误。如果加上大括号,就可以清楚地指示变量的范围,并警告它不是一个简单的重构。 - Andy

22

如果在for循环或if语句中省略大括号,则相同的规则适用。

顺便提一下,如果你反编译编译后的代码,编译器反编译器会添加大括号。


2
一个区别是,嵌套for语句时我们会缩进内部的语句块,但是嵌套的using语句通常是左对齐的。当然,这只是一种常见的惯例,而不是语言语法。 - Steven Sudit
7
+1,挑刺的一点:编译器实际上并不添加大括号,反编译器才会将它们放进去。 - JaredPar

18

就像他说的那样。上面的代码与编写以下内容完全相同:

using (MemoryStream data1 = new MemoryStream()) 
{
    using (MemoryStream data2 = new MemoryStream())
    {
        // Lots of code
    }
}

如果if/else/for/while/using等语句中只有一个命令,可以省略大括号。例如:

// Equivalent!
if (x==6) 
    str = "x is 6";

if(x == 6) {
    str = "x is 6";
}

// Equivalent!
for (int x = 0; x < 10; ++x) z.doStuff();

for (int x = 0; x < 10; ++x) {
    z.doStuff();
}

// NOT Equivalent! (The first one ONLY wraps the p = "bob";!)
if (x == 5) 
p = "bob";
z.doStuff();

if (x == 5) {
   p = "bob";
   z.doStuff();
}

4
这是可行的,但风险很高,因为如果有人后来决定在其他操作之前对data1进行某些处理,他们可能会将其放在使用data1之后,这将使它超出data2使用范围。这可能会导致编译错误,但仍然是一种冒险和无意义的语法快捷方式。

12
那个人可能应该考虑另一种职业。 - spoulson
1
@spoulson:很多人不这样做,但我们的工作仍然是尽可能使代码易于他人维护,以免其他人在我们的代码中引入错误。 - Jimmy Hoffa

3

正如您的同事所说,这相当于嵌套语句。对于 data2 的处理将在 data1 的处理函数之前立即调用。


2

正如一些人所说:如果一个语句后面只有一行代码,那么它可以不用花括号。然而,人们在他们的示例中忽略了这一行可能是一个带有自己花括号的if/using/for语句。以下是一个例子:

if(foo)
  if(bar)
  {
     doStuff();
  }

1
如果语句后只有一条指令,则不需要使用大括号。这就像使用if语句一样。
if(true)
{
   Console.Writeline("hello")
}

意思是相同的

if(true)
   Console.Writeline("hello")

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