为什么不能将值类型的“ this”装箱?

时间:2019-03-12 17:19:44

标签: c# this boxing value-type

因此,我希望能够模仿C#中VB的“具有”功能,并通过StackOverflow遇到了一个相当聪明的解决方案:

public static x with<x>(this x item, Func<x,x> f)
{
    item = f(item);
    return item;
}

要实施,您将执行以下操作:

myObject = myObject.with(myVariable => {
    //do things
});

但是,当我尝试在具有该结构的字段之一的结构中实现此功能时,遇到了麻烦。它说:“结构中的匿名方法[...]无法访问'this'[...] 的成员。”

我对此进行了一些研究,找到了this question的答案,该答案最终指出,不能将值类型的“ this”装箱。在研究了装箱对C#的含义之后,考虑到该函数的参数没有定义的类型,这是有道理的。

我的问题是,为什么不能将值类型的“ this”装箱?

1 个答案:

答案 0 :(得分:1)

这里的要点是,对于struct上的方法,this的含义不是(即您的SomeStruct的值),而是 reference ref SomeStruct,对于in SomeStruct则有效地为readonly struct)。

您不能将这种形式的托管指针装箱-运行时不支持这种情况。托管指针仅应位于堆栈上。实际上,当前在自定义ref SomeStruct中甚至无法包含无法逃避堆栈的ref struct字段。

编译器可以通过假装 作弊来做到这一点-即通过将托管指针从ref SomeStruct解引用到{ {1}},并创建一个捕获上下文,其中SomeStruct被解释为“我们之前取消引用的this”,但是...然后编译器无法保证相同的行为和结果(实际上,我怀疑在SomeStruct场景中可能会遇到这种情况,但是...不引入这种微妙的区别可能会更容易。

相反,编译器建议您有效地手动执行上述步骤;由于编译器不再使用readonly struct进行处理,因此它不再必须假装尊重处理this的通常结果,而只需要保证显式引用的复制值。这就是为什么建议(至少在当前的编译器版本上)的原因:

  

考虑将“ this”复制到匿名方法,lambda表达式或查询表达式之外的局部变量中,并改用局部变量。

因此,大多数lambda,局部方法等可以通过以下实用的步骤来实现:

this

然后在您的lambda /本地方法等中:触摸MyStruct copy = this; // dereference ,而不是触摸Somethingthis.Something。现在,捕获上下文中仅包含copy.Something,并且copy不受copy规则的约束(因为:它不是ref SomeStruct-它是ref SomeStruct)。

确实,但是,这意味着,如果您的意图是变异SomeStruct (并将该可见的就地 ,而不是作为返回值),那么它将无法正常工作。您只需要变异副本(即this)。如果确实存在,这正是编译器必须执行的操作,但至少现在,复制步骤(取消引用)在您的代码中是显式且可见的。