The main point here is that for a method on a struct
, the meaning of this
is not a value (i.e. an value of your SomeStruct
), but rather a reference (a ref SomeStruct
, or effectively an in SomeStruct
in the case of a readonly struct
).
You can't box a managed pointer of this form - that isn't a scenario supported by the runtime. Managed pointers are only meant to be on the stack. In fact, currently you can't even have a ref SomeStruct
field in a custom ref struct
that can't escape the stack.
The compiler could cheat by pretending to do that - i.e. by dereferencing the managed pointer from a ref SomeStruct
into a SomeStruct
and creating a capture context where this
is interpreted as "the SomeStruct
that we dereferenced earlier", but ... then the compiler can't guarantee the same behaviours and outcomes (actually, I suspect a case could be made for this in the readonly struct
scenario, but ... it is probably easier not to introduce that subtle distinction).
Instead, the compiler suggests that you effectively do the above step manually; since the compiler is no longer dealing in terms of this
, it no longer has to pretend to respect the usual outcomes for dealing with this
, and instead only has to guarantee the behaviours of an explicitly dereferenced copy of the value. That's why it advises (at least on current compiler versions):
Consider copying 'this' to a local variable outside the anonymous method, lambda expression or query expression and using the local instead.
Most lambdas, local methods, etc can therefore be achieved by the pragmatic step of:
MyStruct copy = this; // dereference
then in your lambda / local method / etc: instead of touching Something
aka this.Something
- touch copy.Something
. It is now only copy
that is being included in the capture-context, and copy
is not bound by the ref SomeStruct
rules (because: it isn't a ref SomeStruct
- it is a SomeStruct
).
It does, however, mean that if your intent was to mutate this
(and have that visible in-place, rather than as a return value), then it won't work. You'll only be mutating the copy (i.e. copy
). This is exactly what the compiler would have had to do anyway if it had lied, but at least now the copy step (dereference) is explicit and visible in your code.