C# 为什么可以';我们不能在if语句中定义一个变量吗?

C# 为什么可以';我们不能在if语句中定义一个变量吗?,c#,language-design,scope,C#,Language Design,Scope,也许这个问题以前已经被回答过,但是如果这个词经常出现,很难找到它 这个例子没有意义(表达式总是正确的),但它说明了我的问题 为什么此代码有效: StringBuilder sb; if ((sb = new StringBuilder("test")) != null) { Console.WriteLine(sb); } 但该代码不是: if ((StringBuilder sb = new StringBuilder("test")) != null) { Console.

也许这个问题以前已经被回答过,但是如果这个词经常出现,很难找到它

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

为什么此代码有效:

StringBuilder sb;
if ((sb = new StringBuilder("test")) != null) {
    Console.WriteLine(sb);
}
但该代码不是:

if ((StringBuilder sb = new StringBuilder("test")) != null) {
    Console.WriteLine(sb);
}
我发现了一个关于
while
语句的类似问题。这里公认的答案是,在
while
语句中,这意味着变量将在每个循环中定义。但对于我的
if
语句示例,情况并非如此


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

这是因为C语言规范第8.5.1节规定:

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

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

StringBuilder sb = new StringBuilder("test")
实际上,你做的事情与:

StringBuilder sb; sb = new StringBuilder("test")
因此,您对
!=null
,因为赋值不是一个表达式,而是一个语句,它是一个局部变量声明符,由一个标识符和一个表达式组成

语言规范给出了此示例,说明:

void F() {
   int x = 1, y, z = x * 2;
}
完全等同于:

void F() {
   int x; x = 1;
   int y;
   int z; z = x * 2;
}

这与语句和表达式之间的差异有关。表达式有值,而语句没有值

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

StringBuilder sb; // statement

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

StringBuilder sb = new StringBuilder("test"); // statement
请注意,只有中间部分是表达式

现在我们来讨论你的条件语句。使用not equals运算符的语法为

expression != expression
所以在<代码>的两边=你需要一些真正有价值的东西(这才有意义)。因此,运算符的两边都不能有语句。这就是为什么一个版本的代码可以工作,而另一个版本不能工作的原因。

而不是:

if ((StringBuilder sb = new StringBuilder("test")) != null) {
    Console.WriteLine(sb);
}
你也可以这样写:

for (StringBuilder sb = new StringBuilder("test"); sb != null; sb = null) {
    Console.WriteLine(sb);
}
如果变量不为null,则此for循环将执行一次。在循环结束时,临时变量设置为null。然后,循环条件的计算结果为false,执行右大括号后,下一条语句将继续。与您最初的if语句完全相同。

试试看

以您的例子:

if (new StringBuilder("test") is var sb && sb != null) {
    Console.WriteLine(sb);
}
C#7.0能够在条件内部声明
out
变量。与泛型相结合,可以利用它获得所需的结果:

公共静态bool make(out T result),其中T:new(){
结果=新的T();
返回true;
}
// ... 后来:
if(其他条件和制造(输出变量sb)){
某人附加(“你好!”);
// ...
}
您也可以避免使用泛型,而是选择帮助器方法:

public static bool makeStringBuilder(out StringBuilder result, string init) {
    result = new StringBuilder(init);
    return true;
}
// ... and later:
if (otherCondition && makeStringBuilder(out var sb, "hi!")) {
    sb.Append("hello!");
    // ...
}

变量声明是一个语句。条件需要一个有值的表达式,而这个值是语句所没有的,Jeff很在行。请阅读以下两个链接。语句:表达式:@jeffmerca做得很好,然后更改规范,说明变量声明可以用作表达式。这不是不可能的根本原因。C++是这样做的。@这几乎发生在C 6中,但是我理解的时候,它被拉出了,有助于建立一个更好的规范来与模式匹配一起使用。这将在C#的未来版本中发生变化,但在当前版本中不可能发生。请参阅为什么在C#6中删除这一点,我想for循环是一个特例?严格来说,这更多的是说明他们是如何做到的,而不是为什么做到的。我完全看不出为什么“int x”不能在堆栈上保留对变量的引用,也就是说“int x=1”可以像(int x)=1)一样进行逻辑解码——在c#7.0中使用新的元组,这是更愚蠢的,因为它是不允许的(但如果你声明元组的所有元素,它是允许的)。@EskeRahn在一天结束时,每一个这样的问题都有相同的原因——因为设计师做出了这个决定。这描述了决策以及为什么必须采用这种方式来实现以匹配规范。@ReedCopsey这是一个简单的答案。。。。通常,像这样的“为什么”问题可以用它与其他问题之间的冲突来回答。这里不是这样,所以这里没有理由,尽管可以原谅为什么(目前)不允许这样做。@MrPaulch no.for循环有一个初始化语句、一个条件表达式和一个事后思考语句,然后是循环语句。这提供了我要求的语法。唯一的缺点是变量的作用域不是if块,而是为
if
语句所在的同一作用域定义的。@comecme从7/15/16.sly开始选择该选项!整洁的尽管如此,我还是更喜欢构建int-first-class
if(var sb=F();sb!=null)
就像在good old
for(var sb=;sb!=null;)
中一样。另外,天哪,我理解针对具体类型的模式匹配,但模式
是var
只是。。令人捧腹的这几乎就像是有人专门为此目的设计/允许
var
类型推断。这与问题非常相似,但只有经验丰富的程序员才会很快发现您在做什么。不过,这是一个优雅的解决方案。我喜欢这个。它会把每个人都搞糊涂,每次有人读它都会浪费一分钟。