为什么我们不能在if语句中定义变量?

com*_*cme 47 c# scope language-design

也许这个问题之前已经得到了回答,但是这个词if经常出现,很难找到它.

这个例子没有意义(表达式总是正确的),但它说明了我的问题.

为什么此代码有效:

StringBuilder sb;
if ((sb = new StringBuilder("test")) != null) {
    Console.WriteLine(sb);
}
Run Code Online (Sandbox Code Playgroud)

但是这段代码不是:

if ((StringBuilder sb = new StringBuilder("test")) != null) {
    Console.WriteLine(sb);
}
Run Code Online (Sandbox Code Playgroud)

我发现了一个关于while声明的类似问题.那里接受的答案说,在一个while声明中,它意味着变量将在每个循环中定义.但是对于我的if陈述例子,情况并非如此.

那么我们不允许这样做的原因是什么?

Ree*_*sey 38

这是因为C#语言规范的第8.5.1节.状态:

此外,局部变量声明中的变量初始值设定项与声明后立即插入的赋值语句完全对应.

这基本上意味着,当你这样做时:

StringBuilder sb = new StringBuilder("test")
Run Code Online (Sandbox Code Playgroud)

实际上,你做的完全相同:

StringBuilder sb; sb = new StringBuilder("test")
Run Code Online (Sandbox Code Playgroud)

因此,对于您的检查不再有返回值!= null,因为赋值不是单个表达式,而是一个语句,它是一个由标识符后跟表达式组成的局部变量声明符.

语言规范给出了这个例子,说明了这个:

void F() {
   int x = 1, y, z = x * 2;
}
Run Code Online (Sandbox Code Playgroud)

完全等同于:

void F() {
   int x; x = 1;
   int y;
   int z; z = x * 2;
}
Run Code Online (Sandbox Code Playgroud)

  • 我想for循环是一个特例然后呢? (6认同)
  • 严格来说,这更多地说他们是如何做到的,而不是为什么.我完全没有理由为什么"int x"不能在堆栈上留下对变量的引用,即"int x = 1"也可以逻辑解码为"(int x)= 1" - 在c#7.0中的新元组这是更加愚蠢的,它是不允许的(如果你声明元组的所有元素,它是允许的). (3认同)
  • @MrPaulch 不。for 循环有一个初始化语句、一个条件表达式和一个事后语句,然后是循环语句。 (3认同)

Jec*_*oms 24

尝试C#7的模式匹配.

使用你的例子:

if (new StringBuilder("test") is var sb && sb != null) {
    Console.WriteLine(sb);
}
Run Code Online (Sandbox Code Playgroud)

  • 这确实提供了我要求的语法。唯一的缺点是该变量的作用域不限于 if 块,但它定义的作用域与 if 语句所在的作用域相同。 (5认同)
  • 另外,天哪,我理解针对具体类型的模式匹配,但是模式`is var` 只是.. 搞笑。几乎就好像有人专门为此目的设计/允许“var”类型推断 (5认同)
  • @comecme [roslyn 团队笔记](https://github.com/dotnet/roslyn/issues/12939) 从 7/15/16 开始的那个选择。 (2认同)

Ken*_*nde 9

这与语句和表达式之间的区别有关.表达式具有值,而语句不具有值.

使用您的示例,请注意以下分类:

StringBuilder sb; // statement

sb = new StringBuilder("test") // expression

StringBuilder sb = new StringBuilder("test"); // statement
Run Code Online (Sandbox Code Playgroud)

请注意,只有中间部分是表达式.

现在我们转到你的条件语句.使用not-equals运算符的语法是

expression != expression
Run Code Online (Sandbox Code Playgroud)

因此,在!=你需要的东西实际上有一些价值(这才有意义).因此,您不能在运营商的任何一方发表声明.这就是为什么你的代码的一个版本工作,而另一个版本没有.


use*_*316 6

代替:

if ((StringBuilder sb = new StringBuilder("test")) != null) {
    Console.WriteLine(sb);
}
Run Code Online (Sandbox Code Playgroud)

还可以这样写:

for (StringBuilder sb = new StringBuilder("test"); sb != null; sb = null) {
    Console.WriteLine(sb);
}
Run Code Online (Sandbox Code Playgroud)

如果您的变量不为空,则此 for 循环将执行一次。在循环结束时,您的临时变量设置为 null。然后循环条件评估为假,并且在执行右大括号后继续下一条语句。完全按照您的 if 语句最初的意图。

  • 我喜欢这个。它会让每个人都感到困惑,并且每次有人必须阅读它时都会浪费一分钟。 (19认同)
  • 这与问题非常相似,但只有经验丰富的程序员才能很快发现您在做什么。然而,一个优雅的解决方案。 (2认同)