Lambda分配局部变量

Spo*_*ook 11 c# c++ lambda compiler-errors local-variables

请考虑以下来源:

static void Main(string[] args)
{
    bool test;

    Action lambda = () => { test = true; };
    lambda();

    if (test)
        Console.WriteLine("Ok.");
}
Run Code Online (Sandbox Code Playgroud)

它应该编译,对吗?嗯,事实并非如此.我的问题是:根据C#标准,这个代码应该编译还是这个编译器错误?


错误消息:

Use of unassigned local variable 'test'
Run Code Online (Sandbox Code Playgroud)

注意:知道,如何修复错误,我部分知道,为什么会发生.但是,无条件地分配了局部变量,我想,编译器应该注意到,但事实并非如此.我想知道为什么.


评论答案:C#允许声明未分配的变量,这实际上非常有用,即.

bool cond1, cond2;
if (someConditions)
{
    cond1 = someOtherConditions1;
    cond2 = someOtherConditions2;
}
else
{
    cond1 = someOtherConditions3;
    cond2 = someOtherConditions4;
}
Run Code Online (Sandbox Code Playgroud)

编译器正确编译了这段代码,我认为,保留未分配的变量实际上会使代码更好一些,因为:

  • 它告诉读者,稍后会分配值(通常可能在以下条件语句中)
  • 强制程序员在内部条件的所有分支中分配变量(如果从一开始就是这个代码的目的),因为如果其中一个分支没有分配其中一个,编译器将拒绝编译代码.

在边缘:这更有趣.考虑C++中的相同示例:

int main(int argc, char * argv[])
{
    bool test;

    /* Comment or un-comment this block
    auto lambda = [&]() { test = true; };
    lambda();
    */

    if (test)
        printf("Ok.");

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果您对块进行注释,则编译以警告结束:

main.cpp(12): warning C4700: uninitialized local variable 'test' used
Run Code Online (Sandbox Code Playgroud)

但是,如果删除注释,编译器不会发出任何警告.在我看来,它能够确定是否设置了变量.

Ant*_*ram 17

我的问题是:根据C#标准,这个代码应该编译还是这个编译器错误?

这不是一个错误.

C#语言规范(4.0)的第5.3.3.29节概述了有关匿名函数的明确赋值规则,包括lambda表达式.我会在这里发布.

5.3.3.29匿名函数

对于具有正文(块或表达式)主体的lambda表达式或匿名方法表达式expr:

  • 在body之前的外部变量v的明确赋值状态与在expr之前的v的状态相同.也就是说,外部变量的明确赋值状态是从匿名函数的上下文继承的.

  • expr之后的外部变量v的明确赋值状态与expr之前的v的状态相同.

这个例子

delegate bool Filter(int i);

void F() {
    int max;

    // Error, max is not definitely assigned    
    Filter f = (int n) => n < max;

    max = 5;    
    DoWork(f); 
}
Run Code Online (Sandbox Code Playgroud)

生成编译时错误,因为在声明匿名函数的地方没有明确赋值.这个例子

delegate void D();

void F() {    
    int n;    
    D d = () => { n = 1; };

    d();

    // Error, n is not definitely assigned
    Console.WriteLine(n); 
}
Run Code Online (Sandbox Code Playgroud)

因为在匿名函数中对n的赋值对匿名函数外的n的明确赋值状态没有影响,所以也会生成编译时错误.

您可以看到这适用于您的具体示例.test在声明lambda表达式之前未特别指定变量.在执行lambda表达式之前没有特别指定它.并且在lambda表达式执行完成后没有专门分配它.根据规则,编译器不会认为变量在if语句中被读取时被明确赋值.

至于为什么,我只能重复我在这个问题上所阅读的内容,而且只有我能记住的东西,因为我无法生成链接,但C#并不会尝试这样做,因为尽管这是一个微不足道的案例,眼睛可以看到更常见的情况是,这种类型的分析是非常重要的,实际上可能等于解决停止问题.因此,C#"保持简单",并要求您通过更容易应用和可解决的规则来玩.

  • @Spook:在C++中,使用未初始化的变量不是编译时错误,但在运行时,您将获得未定义的行为.你得到的警告只是编译器很好; 它肯定不会试图在每种情况下弄明白. (2认同)

Ton*_*ina 5

您正在使用未分配的变量.即使实际分配了变量,编译器也无法从您发布的代码中推断出该变量.

无论如何都应该初始化所有局部变量,所以这很有趣,但仍然是错误的.

  • @GManNickG:表征情况的更好方法是:(1)确定明确赋值的一般问题是无法解决的,因为它等同于暂停问题,(2)因此,C#规范严格定义了"明确赋值".当变量实际上是明确分配时,有时会报告"未明确分配"的方式,以及(3)在这种特定情况下,过程间流量跟踪算法可以工作,但不会与指定的行为一致. (2认同)