LAMBDA分配局部变量(Lambda assigning local variables)

2019-08-06 02:05发布

考虑以下来源:

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

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

    if (test)
        Console.WriteLine("Ok.");
}

它应该编译吧? 那么,它不是。 我的问题是:按C#标准的,应此代码编译或这是一个编译器错误?


错误信息:

Use of unassigned local variable 'test'

注:我知道 ,如何修正这个错误,我知道一部分 ,为什么它会发生。 然而,局部变量被无条件分配,我想,那编译器应该注意到,但事实并非如此。 我想知道为什么。


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

bool cond1, cond2;
if (someConditions)
{
    cond1 = someOtherConditions1;
    cond2 = someOtherConditions2;
}
else
{
    cond1 = someOtherConditions3;
    cond2 = someOtherConditions4;
}

编译器编译正确的代码,我想,那未分配离开变量实际上使代码更好一点,这是因为:

  • 它告诉读者,值(在下面的条件语句大多可能)分配后
  • 迫使程序员分配的内部条件的所有分支的变量(如果它是从一开始这个代码的目的),因为编译器会拒绝编译代码,如果其中一个分支的不分配其中之一。

在保证金 :这是更有趣。 考虑在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;
}

如果你对此有何评论块出来,编译带有警告结束:

main.cpp(12): warning C4700: uninitialized local variable 'test' used

但是,如果您删除注释,编译器发出任何警告任何责任。 在我看来,它是能够确定,如果变量被设置后所有。

Answer 1:

我的问题是:按C#标准的,应此代码编译或这是一个编译器错误?

这是不是一个错误。

在第5.3.3.29 C#语言规范 (4.0)列出了有关的匿名功能,包括lambda表达式的赋值规则。 我将它张贴在这里。

5.3.3.29匿名函数

对于λ-表达或匿名方法表达式expr具有主体(无论是块或表达)体:

  • 主体之前的外变量v的明确赋值状态是一样的v的位于expr之前的状态。 也就是说,外部变量的明确赋值状态是从匿名函数的上下文中继承。

  • expr之后的外变量v的明确赋值状态是一样的v的位于expr之前的状态。

这个例子

 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); } 

产生自一个最大编译时错误没有明确赋值,匿名函数声明。 这个例子

 delegate void D(); void F() { int n; D d = () => { n = 1; }; d(); // Error, n is not definitely assigned Console.WriteLine(n); } 

还产生一个编译时间错误,因为分配到n在匿名函数没有在匿名函数之外n的明确赋值状态影响。

你可以看到这是如何适用于您的具体的例子。 可变test没有特别lambda表达式的声明之前分配。 它没有特别的lambda表达式的执行之前进行分配。 并且它没有特别的lambda表达式执行完成之后分配。 按规则,编译器不考虑变量在它的点被明确赋值在被读取if声明。

至于为什么,我只能重复我对此事的读取,只有我能记得我不能产生一个链接,但是C#不会尝试这样做,因为,虽然这是一个很小的情况下,眼睛可以看到,这是更为频繁,这种类型的分析将是不平凡的,确实可以达到解决停机问题的情况下。 因此,C#“保持简单”,并要求您更易于应用和解决的规则行事。



Answer 2:

您正在使用未赋值的变量。 尽管实际上为变量赋值,编译器有无法推断,从你的代码已经发布的方式。

反正声明时所有的局部变量应该被初始化,所以这是有趣的,但仍然是错误的。



Answer 3:

当编译器执行的方法的控制流分析,以确定一个可变是否被明确赋值它仅将当前方法的范围之内看。 埃里克利珀讨论了这个在这个博客帖子 。 这是理论上的可能编译器来分析方法,从“当前方法”中调用来思考,当一个变量被明确赋值。

正如我前面提到的,我们可以做间分析,但在实践中得到真正凌乱的真正快。 想象一下,一百相互递归方法,所有进入无限循环,投掷,或拨打该组中的另一种方法。 设计一个编译器,可以从逻辑调用的复杂的拓扑结构推断可达是可行的,但潜在的大量的工作。 另外,如果您对程序的源代码间分析只适用; 如果这些方法之一是在装配什么,首先我们一起工作是元数据?

请记住,你的代码示例是不是忠实地一个方法。 匿名方法将被重构到另一个类,它的一个实例将被创建,它会调用类似于您定义的方法。 此外,该编译器将需要分析的定义delegate类以及定义Action有理由相信,您所提供的方法,实际执行。

因此,尽管它的理论可能的范围内,让编译器知道变量是在这种背景下到达,编译作家故意选择不都因编写编译器为它的复杂性,也(可能显著)增加在时间上,将采取编译程序。



Answer 4:

从ECMA标准第8.3变量和参数的片段:

可以得到其值之前的变量应被分配。 这个例子

class Test
{
    static void Main() {
    int a;
    int b = 1;
    int c = a + b; // error, a not yet assigned

    }
}

导致编译时错误,因为它试图使用该变量一个它被分配一个值之前。 管理明确赋值的规则在§12.3定义。

因此它指出在使用前的变量必须被分配,否则它会导致编译错误。 因为你正在创建一个委托并调用它,即包含在委托调用中的方法在技术上不知道。 因此,编译器不会弄明白。 记住它是委托的Invoke方法正在被不叫实际的方法。

ECMA标准的C#



文章来源: Lambda assigning local variables