为什么在使用new关键字时不需要括号?

3
如果我想获取某个目录中所有文件的列表,可以输入类似以下内容的命令:
var files = (new DirectoryInfo(@"C:\Temp")).GetFiles();

然而,以下代码同样可行(请注意,在“new”关键字之前没有括号):
var files = new DirectoryInfo(@"C:\Temp").GetFiles();

为什么会允许这种情况发生?像这样的陈述:
2 + 4 * 3   and   (2 + 4) * 3

由于括号的存在,14和18分别将被解析出来。这与在静态版本的DirectoryInfo对象上调用".GetFiles()"方法相似(因为没有使用"new"关键字),括号加上"new"关键字使得我明确地使用了DirectoryInfo对象的实例。

也就是说:

2   +  4               * 3
new    DirectoryInfo()   .GetFiles()

vs:

(2   +  4              ) * 3
(new    DirectoryInfo())   .GetFiles()

有没有简单的解释说明为什么会这样呢?对于C#词法分析器来说,处理带括号和不带括号的情况难度是否更大呢?我的比喻有缺陷吗?
5个回答

9

Note: This answer has a lot of expressions which are equivalent to each other. Rather than keep them inline or break them out into two separate lines, I've adopted a shorthand of <==> to mean "is equivalent to". So

foo   <==>   bar

should be read as "the expression foo is equivalent to the expression bar".

您在谈论的是“优先级”。星号*比二元加号+运算符具有更高的优先级,因此它会更紧密地“绑定其操作数”。因此:
 x * y + z   <==>   (x * y) + z

new和“点”部分都是主表达式,它们具有相同的优先级。 当两个形式具有相同的优先级时,会涉及到结合性。在C#规范中,对于new运算符并不是非常清楚,但.运算符是二元运算符,所有二元运算符都被认为是左关联的。换句话说:

x.y.z   <==>   (x.y).z

作为另一个例子,*/ 拥有相同的优先级,因此:
x * y / z   <==>   (x * y) / z
x / y * z   <==>   (x / y) * z

因此:
new DirectoryInfo(x).GetFiles()   <==>   (new DirectoryInfo()).GetFiles()

所有这些都与求值顺序有些分离,求值顺序始终从左到右。例如,在表达式x + y * z中,*的更高优先级意味着:

x + y * z   <==>   x + (y * z)

虽然仍然是先计算x,然后是yz,再进行乘法运算,最后才是加法。像往常一样,Eric Lippert在这个主题上写得非常好


4
请参阅MSDN有关优先级的文章:7.2.1 所有非赋值二元运算符都是左结合的,因此具有相同优先级的new.运算符按从左到右的顺序进行评估。
因此,在给定的表达式中,在调用其方法之前实例化了对象。 另外-new具有相等优先级,因此在.获取之前按从左到右的顺序使用类型标识符。

3
调用静态方法的语法是DirectoryInfo.GetFiles()而不是DirectoryInfo(...).GetFiles(),所以我不确定这两个解释之间怎么可能会有冲突。
无论如何,您提到的数值表达式示例将先乘以3,因为乘法比加法或减法具有更高的优先级。所有括号所做的就是允许您覆盖默认的优先级设置。
即使调用静态方法的语法相同,在您提出的问题中,默认优先级仍然会导致它按预期工作,没有括号也可以实现。

那就是我想说的,但更加雄辩。 - Pow-Ian
不,这与“ordering”无关——它与“precedence”有关。它们并不相同。http://blogs.msdn.com/b/ericlippert/archive/2008/05/23/precedence-vs-associativity-vs-order.aspx - Jon Skeet
@JonSkeet 是的!语义很重要的神话是从哪里来的? - SAJ14SAJ
@JonSkeet:不,它们不是同一件事。但优先级确实会影响评估顺序,这似乎与问题的措辞相关。 - Jonathan Wood
@JonathanWood:为什么要引入求值顺序呢?优先级和结合性是重要的,为什么要混淆这些概念呢?尽管 * 的优先级高于 +,但在表达式 x + y * z 中,仍然先计算 x。我认为区分这两个概念非常重要。 (我不得不编辑自己的回答来澄清它 - 我意识到这很难 - 但值得做...) - Jon Skeet
我没有涉及优先级,因为问题似乎只是在问括号的结合性质在数学和语法方面的区别。这就是为什么我喜欢Jonathan的答案胜过我的原因。它提到了两者。 - Pow-Ian

2

您不能对返回值使用new,所以编译器知道将new与初始化的类型一起使用,并在新实例上运行方法。

(new A()).Foo() // Makes sense! sort of...
new (A().Foo()) // Makes absolute no sense.

(1 + 4) * 10 // Makes sense
1 + (4 * 10) // Also makes sense

数字之间以及C#关键字之间如何协同工作的逻辑是有所不同的。


很抱歉,这是不正确的。表达式的解析是基于运算符优先级而不是语义完成的。实际上,这是由于手持相同优先级的非赋值二进制运算符的从左到右求值。.是一个二进制运算符。new与它具有相同的优先级。 - SAJ14SAJ
我怀疑编译器甚至都不需要,因为语法已经是明确无歧义的。而且 new (A().Foo()) 可以有意义。 - Esailija
但在 new. 的情况下,它们具有相同的优先级,因此它们从左到右进行评估。这在数字的情况下并非如此,我正在展示其中的区别。 - SimpleVar
@YoryeNathan 我道歉,我不是有意的。表达式的语法可以非常微妙,特别是在像C#这样的优先级语法中,而不是像我们的老朋友(如果你和我一样老)Pascal那样,其中正式语法确定了运算符的绑定。 - SAJ14SAJ
@SAJ14SAJ 人们总是会问:“但是,如果C#的语法是在点号之前编写方法和参数,在其上操作的实例之后,那么一切都会混乱不堪,但我敢打赌肯定有解决办法,对吧?”所以我直接进入了这个话题,因为事情并不是盲目地从左到右进行,而是有其逻辑性。 - SimpleVar

1

你的比喻对于数学中的运算顺序是很好的。但在语法上,使用new关键字告诉编译器准备一个类的“新”实例。无论是否使用括号,使用关键字new都会创建该类的新实例。


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