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表达式
答案 0 :(得分:1)
问题是变量的生命周期与使用它的lambda中地址的生命周期。编译器可以确保变量本身与lambda一样长(因为它存储在一个单独的隐藏类中,由于捕获),但是地址本身(可以在事后的其他地方复制)可能比变量,因此会引用一个不再有效的地址。
请注意,这与捕获并用作ref
或out
参数的变量形成对比。例如:
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
投掷了一把猴子扳手。与ref
和out
不同,x
和{{1}}具有编译器可以强制执行并始终安全使用的语义,指针可以以任意方式存储,编译器无法确保这些指针的生命周期。因此,即使它将{{1}}变量捕获到隐藏类中,就像在其他情况下一样,它仍然无法保证类至少与地址保持一致。
您的具体示例在理论上是安全的,因为该地址实际上并未存储在任何地方,并且会被匿名方法调用的方法立即使用。但编译器不能保证安全,因此禁止任何获取捕获变量的地址。
答案 1 :(得分:0)
执行lambda时,内存位置可能已超出范围。您可以在lambda中引用或退出参数的原因相同。 Lambda表达式在初始化时捕获引用变量的值,并在执行时使用捕获的值。如果没有警告,您将捕获x的地址,然后在lambda执行时,您将覆盖其他内容。