C#中的分号

为什么C#中每一行末尾都需要分号? 为什么编译器不能知道每条线的结束位置?

行终止符字符将使您能够跨多行中断语句。

另一方面,像VB这样的语言有一个行继续符 。 我个人认为用分号终止语句而不是继续使用下划线更清晰。

不,编译器不知道换行是用于语句终止,也不应该。 如果您愿意,它允许您将语句带到多行。

看到:

 string sql = @"SELECT foo FROM bar WHERE baz=42"; 

或者大方法重载怎么样:

 CallMyMethod(thisIsSomethingForArgument1, thisIsSomethingForArgument2, thisIsSomethingForArgument2, thisIsSomethingForArgument3, thisIsSomethingForArgument4, thisIsSomethingForArgument5, thisIsSomethingForArgument6); 

相反,分号也允许多语句行:

 string s = ""; int i = 0; 

这是多少陈述?

 for (int i = 0; i < 100; i++) // <--- should there be a semi-colon here? Console.WriteLine("foo") 

需要使用分号来消除歧义。

因此除了内部标识符和关键字等之外,空格并不重要。

我个人同意将一个独特的角色作为行终止符。 它使编译器更容易弄清楚你想要做什么。

与流行的看法相反,100%的时间让编译器找不到一个语句结束而另一个语句没有帮助就开始了! 有些边缘情况是模糊的,无论是单个语句还是跨越多行的多个语句。

阅读Visual Basic技术主管Paul Vick的这篇文章 ,看看为什么它不像听起来那么简单。

严格来说,这是正确的:如果一个人可以找出一个语句结束的地方,那么编译器也是如此。 这还没有真正流行起来,很少有语言实现这种类型的任何东西。 下一版本的VB可能是第一种实现正确处理语句的语言,既不需要显式终止也不需要行继续[ source ]。 这将允许这样的代码:

 Dim a = OneVeryLongExpression + AnotherLongExpression Dim b = 2 * a 

让我们的手指交叉。

另一方面,这确实使解析更加困难,并且可能导致错误消息(参见Haskell)。

也就是说,C#使用类似C语法的原因可能是由于营销原因而不是其他任何原因:人们已经熟悉C,C ++和Java等语言。 无需引入另一种语法。 这有多种原因,但它显然从这些语言中inheritance了许多弱点。

分号的另一个好理由是隔离语法错误。 当出现语法错误时,分号允许编译器返回正轨,以便类似

 a = b + c = d 

可以消除歧义

 a = b + c; = d 

与第二个语句中的错误或

 a = b + ; c = d 

与第一个语句中的错误。 如果没有分号,则无法说明语句在出现语法错误时的结束位置。 缺少括号可能意味着程序的整个后半部分可能被视为一个巨大的语法错误,而不是逐行进行语法检查。

它也有助于另一种方式 – 如果你打算写

 a = b; c = d; 

但是错了并且遗漏了“c”然后没有半成品它看起来像

 a = b = d 

这是有效的,你有一个运行程序与坏和难以找到的错误,所以分号通常可以帮助捕获错误,否则看起来像有效的语法。 另外,我同意每个人的可读性。 出于这个原因,我不喜欢没有某种语句终止符的语言。

我一直在考虑这个问题,如果我可以猜一下语言设计师的动机:

C#显然有分号,因为它来自C的遗产。我最近一直在重读K&R书,很明显Dennis Ritchie真的不想强迫程序员以他认为最好的方式编写代码。 这本书充满了各种评论,“虽然我们对这个问题没有教条,看起来似乎很少使用goto语句,如果有的话”,在关于函数的部分,他们提到他们选择了许多格式样式中的一种,你挑选哪一个并不重要,只要保持一致。

因此,使用显式语句终止符允许程序员根据需要格式化他们的代码。 无论好坏,它似乎与C最初设计的方式一致:按照自己的方式行事。

可以办到。 你所指的是“分号插入”。 JavaScript取得了很大的成功,它不能在C#中应用的原因取决于它的设计者。 也许他们不知道它,或者担心它可能会引起程序员之间的混淆。

有关JavaScript中分号插入的更多详细信息,请参阅指定JavaScript的ECMA脚本标准262 。

我引自第22页(在PDF中,第34页):

  • 当从左到右解析程序时,会遇到令牌输入流的末尾,并且解析器无法将输入令牌流解析为单个完整的ECMA脚本程序,然后在末尾自动插入分号输入流。

  • 当从左到右解析程序时,会遇到某些语法生成所允许的令牌,但是生产是限制生产,并且令牌将是紧跟注释后的终端或非终端的第一个令牌“[没有LineTerminator here]”在限制生产中(并且这样的标记称为限制标记),并且限制标记与之前的标记分开至少一个LineTerminator,然后在分割之前自动插入分号限制令牌。

但是,前面的规则还有一个重要的条件:如果分号将被解析为空语句,或者如果该分号将成为for语句标题中的两个分号之一,则永远不会自动插入分号(section 12.6.3)。

[…]

规范文档甚至包含示例!

我想说,在每个语句之后必须使用分号的最大原因是熟悉C,C ++和/或Java的程序员的熟悉程度。 C#从这些语言inheritance了许多语法选择,而不是简单地命名它们。 以分号结尾的语句只是从这些语言中借用的众多语法选择之一。

您可以准确地说,要求使用分号来终止语句是多余的。 从技术上讲,可以从C#语言中删除分号,但仍然可以使用分号。 问题在于它为人类的误解留下了空间。 我认为分号的必要性是为了人类而不是编译器的歧义。 如果没有某种forms的陈述划界,那么人类就难以解释这样的构思:

 int i = someFlag ? 12 : 5 int j = i + 3 

编译器应该能够很好地处理这个问题,但对于人类来说,下面看起来要好得多

 int i = someFlag ? 12 : 5; int j = i + 3; 

当冒号程序员经常想通过在一行上组合语句来节省空间时,分号是C语言的残余。 即

 int i; for( i = 0; i < 10; i++ ) printf("hello world.\n"); printf("%d instance.\n", i); 

它还帮助了编译器,它不够智能,只能简单地推断语句的结束。 几乎在所有情况下,出于可读性的原因,大多数c#开发人员并不赞成在一行上组合语句。 以上内容通常是这样编写的:

 int i; for( i = 0; i < 10; i++ ) { printf("hello world.\n); printf("%d instance.\n", i); } 

非常冗长! 对于现代语言,可以轻松开发编译器来推断语句的结尾。 C#可以改成另一种语言,除了空格和缩进选项卡之外不使用不必要的分隔符,即

 int i for i=0 i<10 i++ printf "hello world.\n" printf "%d instance.\n" i 

这肯定会节省一些打字,看起来更整洁。 如果使用缩进而不是空格,则代码变得更具可读性。 如果我们允许推断类型,我们可以做得更好,并为其进行特殊情况的阅读(对于[value] = [初始值]到[最终值:

 for i=1 to 10 // i is inferred to be an integer printf "hello world.\n" printf "%d instance.\n" i 

现在,它开始看起来像f#和f#,在某些方面,几乎就像c#没有不必要的标点符号。 但是f#缺少这么多附加function(比如特殊的.NET语言结构,代码完成和良好的智能感知)。 所以,最后f#可以比c#或VB.NET更多的工作来实现,遗憾的是。

就个人而言,我的工作需要VB.NET,我更乐意不用处理分号。 C#是一种过时的语言。 Linq允许我减少我必须编写的代码行数。 不过,如果我有时间,我会编写一个c#的版本,它具有f#的许多function。