Why?
Because of the way your struct
is lifted onto the state machine.
This is what ChangeAsync
actually looks like:
[DebuggerStepThrough, AsyncStateMachine(typeof(Program.Structure.<ChangeAsync>d__4))]
public Task ChangeAsync(int iValue)
{
Program.Structure.<ChangeAsync>d__4 <ChangeAsync>d__;
<ChangeAsync>d__.<>4__this = this;
<ChangeAsync>d__.iValue = iValue;
<ChangeAsync>d__.<>t__builder = AsyncTaskMethodBuilder.Create();
<ChangeAsync>d__.<>1__state = -1;
AsyncTaskMethodBuilder <>t__builder = <ChangeAsync>d__.<>t__builder;
<>t__builder.Start<Program.Structure.<ChangeAsync>d__4>(ref <ChangeAsync>d__);
return <ChangeAsync>d__.<>t__builder.Task;
}
The important line is this:
<ChangeAsync>d__.<>4__this = this;
The compiler lifts a copy of your struct into its state-machine, effectively updating its copy with the value 45. When the async method completes, it has mutated the copy, while the instance of your struct remains the same.
This is somewhat an expected behavior when dealing with mutable structs. That's why they tend to be evil.
How do you get around this? As I don't see this behavior changing, you'll have to create a class
instead of a struct
.
Edit:
Posted this as an issue on GitHub. Received a well educated reply from @AlexShvedov, which explains a bit deeper the complexity of structs and state machines:
Since execution of every closure can be arbitrarily delayed, we need
some way to also delay the lifetime of all the members captured into
closure. There is no way to do it in general for this of value type,
since value type can be allocated on stack (local variables of value
types) and stack space will be reused on method execution exit.
In theory, when value type is stored as a field of some managed
object/element of array, C# can emit closure code to do struct
mutation inplace. Unfortunately, there is no knowledge on where this
value is located when emitting struct member code, so C# decided
simply to force users handle this situation manually (by copying the
this value most of the time, as error message suggested).
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…