为什么我不能将变量的地址传递给匿名函数?

时间:2018-01-10 00:23:14

标签: c# .net

unsafe class Program
{

    static void Main(string[] args)
    {
        int x;
        Thread t = new Thread(() => {  sum(12, 6, &x); }); // can't pass adrees of x
    }

    static unsafe void sum(int a ,int b,int* p)
    {
        *p = a + b;
    }

}

引发错误:

  

错误CS1686:本地' x'或其成员   无法获取其地址并在匿名方法中使用   或lambda表达式

2 个答案:

答案 0 :(得分:1)

问题是变量的生命周期与使用它的lambda中地址的生命周期。编译器可以确保变量本身与lambda一样长(因为它存储在一个单独的隐藏类中,由于捕获),但是地址本身(可以在事后的其他地方复制)可能比变量,因此会引用一个不再有效的地址。

请注意,这与捕获并用作refout参数的变量形成对比。例如:

class Program
{

    static void Main(string[] args)
    {
        int x;
        Thread t = new Thread(() => {  sum(12, 6, out x); });
    }

    static void sum(int a, int b, out int p)
    {
        p = a + b;
    }    
}

以上是允许的,因为捕获的变量将从堆栈移动到一个保存它的单独类,并且该类的生命周期可以确保至少与将使用它的委托一样长。

unsafe投掷了一把猴子扳手。与refout不同,x和{{1}}具有编译器可以强制执行并始终安全使用的语义,指针可以以任意方式存储,编译器无法确保这些指针的生命周期。因此,即使它将{{1}}变量捕获到隐藏类中,就像在其他情况下一样,它仍然无法保证类至少与地址保持一致。

您的具体示例在理论上是安全的,因为该地址实际上并未存储在任何地方,并且会被匿名方法调用的方法立即使用。但编译器不能保证安全,因此禁止任何获取捕获变量的地址。

答案 1 :(得分:0)

执行lambda时,内存位置可能已超出范围。您可以在lambda中引用或退出参数的原因相同。 Lambda表达式在初始化时捕获引用变量的值,并在执行时使用捕获的值。如果没有警告,您将捕获x的地址,然后在lambda执行时,您将覆盖其他内容。

相关问题