【问题标题】:C# 7.0 pattern matching variables' scope and assignment syntaxC# 7.0 模式匹配变量的范围和赋值语法
【发布时间】:2019-03-16 13:42:51
【问题描述】:

前:Pattern match variable scope,但我想了解更多关于分配行为的信息。

我做了以下测试。

在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 的范围内分配。

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

【问题讨论】:

    标签: c# c#-7.0


    【解决方案1】:

    来自spec for patterns in C# 7.0

    模式变量的范围

    模式中声明的变量的作用域如下:

    • 如果模式是 case 标签,那么变量的范围是 case 块

    否则变量在 is_pattern 表达式中声明,其范围基于立即包含 is_pattern 表达式的表达式的构造,如下所示:

    • 如果表达式在表达式体 lambda 中,则其范围是 lambda 体。
    • 如果表达式位于表达式主体的方法或属性中,则其范围是方法或属性的主体。
    • 如果表达式位于catch 子句的when 子句中,则其范围为catch 子句。
    • 如果表达式在 iteration_statement 中,则其范围就是该语句。
    • 否则,如果表达式是某种其他语句形式,则其范围是包含该语句的范围。

    为了确定范围,embedded_statement 被认为是在它自己的范围内。例如,if_statement 的语法是

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

    因此,如果 if_statement 的受控语句声明了一个模式变量,则其范围仅限于该 embedded_statement

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

    在这种情况下,z 的范围是嵌入语句M(y is var z);

    其他情况是由于其他原因导致的错误(例如,在参数的默认值或属性中,这两者都是错误,因为这些上下文需要常量表达式)。

    在 C# 7.3 中,我们添加了以下可以声明模式变量的上下文: - 如果表达式在构造函数初始化器中,它的作用域是构造器初始化器和构造器的主体。 - 如果表达式在字段初始值设定项中,则其范围是它出现的 equals_value_clause。 - 如果表达式位于指定要转换为 lambda 主体的查询子句中,则其范围就是该表达式。

    【讨论】:

    • 我刚才搜索了'assign',发现第一部分确实是在谈论分配。它说'在 is 运算符为真之后分配变量',但根据我的测试,它实际上是由整个 if_condition 确定的
    • 在您的示例中查看F1F3,它们都与规范兼容。分析is的逻辑了解了两个分支中哪个有赋值/未赋值变量,if只知道真分支进入第一个块,假分支进入else块。 if 没有任何自己的赋值逻辑。
    【解决方案2】:

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

    o is int i
    

    如果 o 可以转换为 int 类型,则此语句将返回 true 并且将设置 i 变量。如果不是,它将返回false 并且i 变量不会被初始化。此外,在 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.
    }
    

    【讨论】:

    • F3 中的最后一条语句确实没有编译。这是因为我导致了形式错误,所以编译器隐藏了后者。我已经编辑了我的示例。
    • 而在F4中我认为解释也应该是“我没有在所有代码路径中初始化”因为我在F4中添加的新案例。
    • 是的,在更新的F4 中,如果转换不成功,您将在打印之前没有初始化i,因此您应该在所有代码路径中对其进行初始化以使其可编译。希望这会有所帮助
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-04-06
    • 2017-09-29
    • 2019-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-27
    相关资源
    最近更新 更多