为什么结构体内的匿名方法无法访问’this’的实例成员

我有一个如下代码:

struct A { void SomeMethod() { var items = Enumerable.Range(0, 10).Where(i => i == _field); } int _field; } 

…然后我得到以下编译器错误:

 Anonymous methods inside structs can not access instance members of 'this'. 

任何人都可以解释这里发生了什么。

通过引用捕获变量(即使它们实际上是值类型;然后完成装箱)。

但是,ValueType(struct)中的这个不能被装箱,因此您无法捕获它。

Eric Lippert有一篇关于捕获ValueTypes的惊喜的文章。 让我找到链接

  • 关于价值类型的真相

请注意Chris Sinclair的评论:

作为快速修复,您可以将结构存储在局部变量中: A thisA = this; var items = Enumerable.Range(0, 10).Where(i => i == thisA._field); A thisA = this; var items = Enumerable.Range(0, 10).Where(i => i == thisA._field); – Chris Sinclair 4分钟前

请注意这会产生令人惊讶的情况: thisA身份this 。 更明确地说,如果你选择将lambda保持更长时间,它将具有通过引用捕获的盒装副本 thisA ,而不是调用SomeMethod的实际实例。

当你有一个匿名方法时,它将被编译成一个新类,该类将有一个方法(你定义的方法)。 它还将引用您使用的每个变量,这些变量超出了匿名方法的范围。 重要的是要强调它是该变量的参考,而不是副本。 正如俗语所说,“lambdas关闭变量,而非价值”。 这意味着如果您关闭lambda范围之外的变量,然后在定义匿名方法之后(但在调用它之前)更改该变量,那么当您调用它时,您将看到更改的值)。

那么,所有这一切的重点是什么。 好吧,如果你要关闭一个结构(这是一个值类型),那么lambda可能比结构更长。 匿名方法将在一个类中 ,而不是一个struct,因此它将在堆上运行,只要它需要就可以生存,并且您可以随意在任何地方(直接或间接)传递对该类的引用。

现在假设我们有一个局部变量,其结构类型就是你在这里定义的。 我们使用这个命名方法生成一个lambda,让我们暂时假设返回了查询items (而不是方法为void )。 然后可以将该查询存储在另一个实例(而不是本地)变量中,并在稍后的另一个方法上迭代该查询。 这会发生什么? 从本质上讲,一旦不再在范围内,我们就会对堆栈上的值类型进行引用。

那是什么意思? 答案是, 我们不知道 。 (请查看链接;这有点是我的论点的关键。)数据可能恰好相同,它可能已经被清零,它可能被完全不同的对象填充,没有办法知道。 作为一种语言,C#不遗余力地阻止你做这样的事情。 诸如C或C ++之类的语言并不会如此难以阻止你自己拍脚。

现在,在这种特殊情况下,你可能不会在this引用的范围之外使用lambda,但编译器不知道,如果它允许你创建lambda它就没办法了确定是否以可能导致其暴露的方式暴露它,因此防止此问题的唯一方法是禁止某些实际上没有问题的情况。