C# 7.0 模式匹配变量的作用域和赋值语法

C# 7.0 pattern matching variables' scope and assignment syntax

前:,但我想了解更多关于赋值行为的信息。

我做了以下测试

在F1中,好像i在范围内,但没有赋值,我按F2可以理解。

但是F3的情况让我很疑惑,因为F2无法解释

然后在F4中显示!在这种情况下没有效果。

// inside a class
object o = 1;
void F0() { // normal usage
    if(o is int i)
        WriteLine(i);
}
void F1() {
    if (o is int i)
        WriteLine(i);
    else
        WriteLine(i); // Use of unassigned local variable 'i'
    WriteLine(i); // the same as above
}
void F2() {
    int i;
    if (o is int) {
        i = (int)o; // just for simulation because 'as' can't unbox
        WriteLine(i);
    }
    else
        WriteLine(i); // Use of unassigned local variable 'i'
    WriteLine(i); // Use of unassigned local variable 'i'
}
void F3() {
    if (!(o is int i))
        WriteLine(i); // Use of unassigned local variable 'i'
    else
        WriteLine(i); // compile
    WriteLine(i); // Use of unassigned local variable 'i'
}
void F4() {
    _ = !(o is int i);
    Console.WriteLine(i); // Use of unassigned local variable 'i'

    _ = o is int i;
    Console.WriteLine(i); // Use of unassigned local variable 'i'
}

我只能得出结论,此语法对 if 的处理方式不同,如果 if condition 为真,它将在 if true 的范围内分配,否则将在 if else的范围。

我的理解正确吗?(我猜不是

让我们仔细看看下面的语句:

o is int i

如果 o 可以转换为 int 类型,此语句将设置 return truei 变量。如果没有,它将 return falsei 变量不会被初始化。此外,在 if 语句中使用此类代码会将变量移动到外部范围。

让我们来看看你的方法:

void F1() {
   if (o is int i)
        WriteLine(i);  // i was initialized, because o is int i returned true
    else
        WriteLine(i); // i was NOT initialized, so you have using of unassigned local variable 'i' here
    WriteLine(i); // the same as above, because i wasn't initialized in all code paths before this statement
}

void F2() {
    int i;
    if (o is int) {
        i = (int)o; // just for simulation because 'as' can't unbox
        WriteLine(i); // i was initialized in previous line
    }
    else
        WriteLine(i); // o is not int, so i wasn't initialized => using of unassigned local variable 'i'
    WriteLine(i); // i wasn't initialized in all code paths, using of unassigned local variable 'i'
}

void F3() {
    if (!(o is int i))
        WriteLine(i); // Using of unassigned local variable 'i' because o can't be casted to int => !(o is int i)
    else
        WriteLine(i); // compile - i was initialized
    WriteLine(i); // you wrote this statement can be compiled, in fact not, because i is not initialized in all code paths
}

void F4() {
    _ = (!(o is int i));
     Console.WriteLine(i); // Use of unassigned local variable 'i', because in case of unsuccessful casting i won't be intialized.
}

来自spec for patterns in C# 7.0

Scope of pattern variables

The scope of a variable declared in a pattern is as follows:

  • If the pattern is a case label, then the scope of the variable is the case block.

Otherwise the variable is declared in an is_pattern expression, and its scope is based on the construct immediately enclosing the expression containing the is_pattern expression as follows:

  • If the expression is in an expression-bodied lambda, its scope is the body of the lambda.
  • If the expression is in an expression-bodied method or property, its scope is the body of the method or property.
  • If the expression is in a when clause of a catch clause, its scope is that catch clause.
  • If the expression is in an iteration_statement, its scope is just that statement.
  • Otherwise if the expression is in some other statement form, its scope is the scope containing the statement.

For the purpose of determining the scope, an embedded_statement is considered to be in its own scope. For example, the grammar for an if_statement is

if_statement
    : 'if' '(' boolean_expression ')' embedded_statement
    | 'if' '(' boolean_expression ')' embedded_statement 'else' embedded_statement
    ;

So if the controlled statement of an if_statement declares a pattern variable, its scope is restricted to that embedded_statement:

if (x) M(y is var z);

In this case the scope of z is the embedded statement M(y is var z);.

Other cases are errors for other reasons (e.g. in a parameter's default value or an attribute, both of which are an error because those contexts require a constant expression).

In C# 7.3 we added the following contexts in which a pattern variable may be declared: - If the expression is in a constructor initializer, its scope is the constructor initializer and the constructor's body. - If the expression is in a field initializer, its scope is the equals_value_clause in which it appears. - If the expression is in a query clause that is specified to be translated into the body of a lambda, its scope is just that expression.