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

也许这个问题以前就有人回答过,但是 if这个词出现得太频繁了,以至于很难找到它。

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

为什么这个代码有效:

StringBuilder sb;
if ((sb = new StringBuilder("test")) != null) {
Console.WriteLine(sb);
}

但这个代码不是:

if ((StringBuilder sb = new StringBuilder("test")) != null) {
Console.WriteLine(sb);
}

我发现了一个关于 while声明的类似问题。公认的答案是,在 while语句中,这意味着变量将在每个循环中定义。但对于我的 if语句示例,情况并非如此。

那为什么我们不能这么做呢?

74616 次浏览

This is because section 8.5.1 of the C# language spec. states:

Furthermore, a variable initializer in a local variable declaration corresponds exactly to an assignment statement that is inserted immediately after the declaration.

This basically means that, when you do:

StringBuilder sb = new StringBuilder("test")

You're, in effect, doing the exact same thing as:

StringBuilder sb; sb = new StringBuilder("test")

As such, there is no longer a return value for your check against != null, as the assignment isn't a single expression, but rather a statement, which is a local-variable-declarator comprised of an identifier followed by an expression.

The language specification gives this example, stating that this:

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

Is exactly equivalent to:

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

This has to do with the difference between a statement, and an expression. An expression has a value, whereas a statement does not.

Using your examples, notice these classifications:

StringBuilder sb; // statement


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


StringBuilder sb = new StringBuilder("test"); // statement

Notice that only the middle portion is a expression.

Now we move onto your conditional statement. The syntax for using the not-equals operator is

expression != expression

So on both sides of the != you need something that actually has a value (this just makes sense). Ergo, you cannot have statements on either side of the operator. This is why the one version of your code works, while the other does not.

Instead of:

if ((StringBuilder sb = new StringBuilder("test")) != null) {
Console.WriteLine(sb);
}

One could also write:

for (StringBuilder sb = new StringBuilder("test"); sb != null; sb = null) {
Console.WriteLine(sb);
}

This for loop will execute once if your variable is not null. At the end of the loop, your temporary variable is set to null. The loop condition then evaluates to false, and the next statement continues after the closing brace is executed. Exactly as your if statement originally intended.

Try C#7's Pattern Matching.

Using your example:

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

C# 7.0 introduced ability to declare out variables right inside conditions. In combination with generics, this can be leveraged for the requested result:

public static bool make<T> (out T result) where T : new() {
result = new T();
return true;
}
// ... and later:
if (otherCondition && make<StringBuilder>(out var sb)) {
sb.Append("hello!");
// ...
}

You can also avoid generics and opt for a helper method instead:

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!");
// ...
}

I was redirected from another "duplicated" question: Declare variable in one line if statement . I do not think answers of this question can properly cover it.

If you are looking for a way to avoid repeating very long path and find it's impossible to define a variable inside if(), try also :

    var email = User.Current.Very.Complex.Path?.Email??"default@mail.com";

it equals to:

    string email = null;
if (User.Current.Very.Complex.Path != null)
email = User.Current.Very.Complex.Path.Email
if (email == null)
email = "default@mail.com";