C# 当涉及可变值类型时,如何处理async/Wait产生的副作用? 请考虑下面的示例代码: using System.Diagnostics; using System.Threading.Tasks; public struct AStruct { public int Value; public async Task SetValueAsync() { Value = await Task.Run(() => 1); } public void SetValue() { Value = 1; } } class Program { static void Main(string[] args) { Test(new AStruct()); TestAsync(new AStruct()).Wait(); } private static async Task TestAsync(AStruct x) { Debug.Assert(x.Value == 0); await x.SetValueAsync(); Debug.Assert(x.Value == 0); } private static void Test(AStruct x) { Debug.Assert(x.Value == 0); x.SetValue(); Debug.Assert(x.Value == 1); } }

C# 当涉及可变值类型时,如何处理async/Wait产生的副作用? 请考虑下面的示例代码: using System.Diagnostics; using System.Threading.Tasks; public struct AStruct { public int Value; public async Task SetValueAsync() { Value = await Task.Run(() => 1); } public void SetValue() { Value = 1; } } class Program { static void Main(string[] args) { Test(new AStruct()); TestAsync(new AStruct()).Wait(); } private static async Task TestAsync(AStruct x) { Debug.Assert(x.Value == 0); await x.SetValueAsync(); Debug.Assert(x.Value == 0); } private static void Test(AStruct x) { Debug.Assert(x.Value == 0); x.SetValue(); Debug.Assert(x.Value == 1); } },c#,struct,async-await,C#,Struct,Async Await,注意Test和TestAsync之间的区别。此代码满足所有断言 我想用Reflector查看代码会告诉我原因,但这仍然是我完全没有预料到的 当然,将AStruct更改为类而不是结构确实会使TestAsync中的第二个断言失败,正如我最初所期望的那样 我的问题是,除了不将可变结构与async/await一起使用之外,还有一种优雅的方法可以让它们和平共处吗?对于结构的异步方法来说,变异“自身”是不可能的 当你仔细考虑时,这当然是完全有道理的。当您在该结构中等待的任何任务实际完成时,假设您已经返回调用

注意
Test
TestAsync
之间的区别。此代码满足所有断言

我想用Reflector查看代码会告诉我原因,但这仍然是我完全没有预料到的

当然,将
AStruct
更改为类而不是结构确实会使
TestAsync
中的第二个断言失败,正如我最初所期望的那样


我的问题是,除了不将可变结构与async/await一起使用之外,还有一种优雅的方法可以让它们和平共处吗?

对于
结构的
异步
方法来说,变异“自身”是不可能的

当你仔细考虑时,这当然是完全有道理的。当您在该结构中等待的任何任务实际完成时,假设您已经返回调用方并允许他们继续执行各种操作,那么您就无法确保调用该方法的实际结构实例已经存在。如果对局部变量调用
SetValueAsync
的方法未对其进行
Wait
Wait
或类似操作,则当
SetValueAsync
到达调用
Run
的继续时,该局部变量的生存期可能已经结束。它不能改变变量,因为变量的生命周期可能在范围内,也可能不在范围内。这里唯一的选项是结构的
async
方法在调用该方法时有效地复制自己,并使continuation中的代码引用一个与调用
async
的变量完全不同的变量。由于该方法制作的副本除了此
async
方法的主体之外,在其他任何地方都无法访问,这意味着,出于所有目的,一个结构的
async
方法永远不能对该结构进行变异(并且任何其他人都可以看到变异)

您可以有一个可变结构的
异步
方法,只要该方法本身不需要改变
结构
。这一方法将需要返回一个带有新结构的
任务
,或者类似的东西


作为一个有趣的探戈,如果
struct
async
方法真的想在该方法的第一个
wait
之前进行变异,那么它在技术可能性的范围内。编译器选择立即获取副本,因此这实际上是不可能的,但明确的选择是在方法的一开始创建副本,而不是仅在第一次
等待后创建副本。这可能是最好的选择,无论这是否是有意的决定,否则会非常混乱。

当值类型是参数时(不带
ref
)对其进行变异是完全没有意义的。异步甚至没有出现在这张图中。@leppie问题是
SetValueAsync
没有变异调用它的变量,而且它没有变异,因为它是
Async
方法。它是在一个变量上调用的,而该变量是一个未通过
ref
传递的参数,这一事实根本不相关。变异甚至无法变异
TestAsync
对在
Main
@Servy中创建的结构所做的副本:猜测生成器也会发生同样的情况(可能是将其用作自由变量时)无论您是否正在使用
异步
/
等待
。此外,异步代码本质上是功能性的,因此处理副作用有点笨拙(返回复合结果而不是使用
async
方法更新值作为副作用通常更简洁)。