为什么C#允许在没有前置语句的情况下使用{}代码块?

88

C#为什么允许没有前置语句(如ifelseforwhile)的代码块?

void Main()
{
    {   // any sense in this?
        Console.Write("foo");
    }
}

71
它为什么不应该呢? - Jamiec
32
答案表明,“让它发生因为它不会伤害到任何人”并不是唯一的含义,这也是提出这样的问题的意义所在。 - Seldon
8
+1个问题听起来很简单,但是答案确实教会了我很有价值的东西。 - Nicolas78
7
@Akash:如果不去问为什么,我们就永远无法变得更好。 - richard
7
@Akash: 这似乎是一种高不可攀的精英主义态度。人们总会有问题,如果没有问为什么的理由,那SO就没有存在的意义。这个网站并不仅仅是让人们解决我们的紧急问题(尽管确实会发生这种情况),而是提供一个问题和答案的存储库,将帮助我们成为更好的程序员。SO是用来问“为什么”的! :-) - richard
显示剩余16条评论
9个回答

149

{ ... } 至少具有引入新的本地变量作用域的副作用。

我倾向于在 switch 语句中使用它们,为每个 case 提供不同的作用域,并以此方式允许我在最接近其使用位置定义同名的本地变量,并标识它们仅在 case 级别有效。


6
这篇文章值得一读:http://blogs.msdn.com/b/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx。 - Dan Diplo
6
如果你的测试用例需要作用域,那么它们太大了!抽取方法。 - Jay Bazuzi
21
@Jay Bazuzi,仅仅因为我想在多个情况下使用相同名称的局部变量,并不意味着它们必须很大。你只是过于草率地做出了“巨大”的结论... :) - João Angelo
恭喜你获得了“出色回答”徽章! :) - BoltClock
1
@BoltClock,谢谢,我只需要更多关于{}的问题来获得那些金徽章... :) - João Angelo

90
在您提供的上下文中,这并没有什么意义。将一个常量字符串写入控制台在程序流的任何位置都将以相同的方式工作。
相反,您通常使用它们来限制某些本地变量的范围。 这里这里 进一步阐述了这一点。请查看 João Angelo 的答案Chris Wallis 的答案 获取简要示例。我相信同样适用于其他具有 C 风格语法的语言,尽管它们与此问题无关。

1 当然,除非你想要有趣并创建自己的Console类,其中包含一个完全意想不到的Write()方法。


24
通常情况下是这样的,但并非总是如此。JavaScript是一个显著的例外;在特定代码块中声明局部变量并不会将其作用域限制在该块内。 - Eric Lippert
@EricLippert:在C#中,在块内声明变量是否会导致变量从运行时的角度进行作用域限定?据我所知,变量的生命周期通常不受作用域块的限制,这一事实在混合语言项目中是可观察到的,如果在循环中对变量的第一件事是将其作为“out”参数传递给另一种语言编写的接口实现,并且该实现在写入之前读取变量。 - supercat
在C++中由于RAII而不是GC,它更加有用。 - Deduplicator
在C++中,它的作用类似于try/catch块,因此在离开该作用域时,在其中声明的变量和类会从堆栈中弹出。这有助于防止名称冲突并减少函数的内存占用。它还会将这些变量从代码编辑器的自动完成中移除。通常情况下,我发现try/catch块更有用。 - Kit10

57

这不是 C# 的一个 特性,而是许多使用大括号来 定义作用域 的 C 语法语言的逻辑副作用。

在你的示例中,大括号根本没有任何影响,但在以下代码中它们定义了变量的作用域和可见性:

这是允许的,因为 i 在第一个块中已经超出作用域,在下一个块中被重新定义:

{
    {
        int i = 0;
    }

    {
        int i = 0;
    }
}

这样是不允许的,因为i已经超出了其作用域并且在外部作用域中不再可见:

{
    {
        int i = 0;
    }

    i = 1;
}

等等,等等。


1
我已经了解到在各种英语风格中括号命名存在差异,但是在哪种风格中,{} 被称为圆括号? - BoltClock
好问题!我猜是十年前我的导师把它灌输到我的脑海中的。我应该称它们为“花括号”或“大括号”。各有各的喜好...http://en.wikipedia.org/wiki/Bracket#Parentheses_.28_.29.2C_.5B_.5D.2C_or_.7B_.7D - Chris Wallis
10
在我的词汇表中,'('代表圆括号,'{'代表花括号,'['代表方括号。 - pb.
坚持一下,我会称它们为花括号,维基百科也包括这个名称。《牛津英语词典》将括号定义为: 一种由[ ]或( )形成的两个标记,在数学中也使用{},用于将单词或多个单词、部分数学公式或类似内容括起来,以使其与上下文分开; 无论如何,它们不是圆括号,但"花括号"似乎可以。 - dumbledad
IME,"花括号"和"方括号"。 - cp.engr

18

我认为{}是一个可以包含多个语句的语句。

考虑一个由布尔表达式后跟一个语句组成的if语句。 这将起作用:

if (true) Console.Write("FooBar");
这也可以起作用:
if (true)
{
  Console.Write("Foo");
  Console.Write("Bar");
}

如果我没记错的话,这被称为块语句。

由于{}可以包含其他语句,因此它还可以包含其他{}。 变量的作用域由其父级{}(块语句)定义。

我要表达的重点是{}只是一个语句,因此不需要if或任何其他东西。


+1 这正是在C风格语言中大括号的作用 - 所谓的“复合语句”。 - Michael Ekstrand

13
在C语法类的编程语言中,一般规则是“任何在{ }之间的内容应被视为单个语句,并可以放置在任何单个语句可以出现的地方”,包括:
  • if语句之后。
  • forwhiledo循环之后。
  • 代码的任何位置
可以理解为语言的语法本身就包含了这些规则。
     <statement> :== <definition of valid statement> | "{" <statement-list> "}"
<statement-list> :== <statement> | <statement-list> <statement>

也就是说,"语句可以由不同的元素组成或由一个左括号、后面跟着一个语句列表(可能包含一个或多个语句),然后是右括号"。也就是说,"{ }块可以替换任何语句的位置",包括在代码中间。

如果不允许在任何语句可以放置的地方使用{ }块,实际上会使语言的定义 更加复杂


有人刚刚提升了这个答案(9年后),它实际上并不是对这个问题的具体回答,而更多地是一次通用讨论。它可能已经被语言和库所取代...但无论如何,这仍然是一种愉悦,谢谢。 - Massimo

1
// if (a == b)
// if (a != b)
{
    // do something
}

1
因为C++(和Java)允许没有前置语句的代码块。
C++之所以允许这样做,是因为C语言也允许。
你可以说这一切归结于美国程序语言(基于C语言)设计的胜利,而不是欧洲程序语言(基于Modula-2)设计的胜利。
(控制语句作用于单个语句,语句可以分组以创建新语句)

你是指 Module2 还是 Modula2? - Richard Ev
确实,这是正确的答案。此外,我只会回答“因为每种计算机语言都是如此”。 - Fattie

0
你问为什么C#允许没有前置语句的代码块。这个问题“为什么”也可以被解释为“这种结构可能有哪些好处?”

就我个人而言,在C#中使用无语句的代码块可以大大提高其他开发人员的可读性,同时要记住代码块限制了局部变量的作用域。例如,考虑以下代码片段,由于额外的代码块,它更容易阅读:

OrgUnit world = new OrgUnit() { Name = "World" };
{
    OrgUnit europe = new OrgUnit() { Name = "Europe" };
    world.SubUnits.Add(europe);
    {
        OrgUnit germany = new OrgUnit() { Name = "Germany" };
        europe.SubUnits.Add(germany);

        //...etc.
    }
}
//...commit structure to DB here

我知道这个问题可以通过为每个结构级别使用方法来更优雅地解决。但是请记住,像样本数据种子生成器之类的东西通常需要快速执行。

因此,尽管上面的代码是按顺序执行的,但代码结构代表了对象的“真实世界”结构,从而使其他开发人员更容易理解、维护和扩展。


0

1因为它可以维护语句或函数的作用域,所以对于管理大量代码非常有用。

{
    {
        // Here this 'i' is we can use for this scope only and out side of this scope we can't get this 'i' variable.

        int i = 0;
    }

    {
        int i = 0;
    }
}

enter image description here


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