当涉及到可变值类型时,如何处理async/await产生的副作用?

mar*_*ark 12 c# struct async-await

请考虑以下示例代码:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

注意之间的差异TestTestAsync.此代码满足所有断言.

我想用反射器查看代码会告诉我原因,但这仍然是我根本没想到的.

当然,AStruct改为成为一个类而不是一个结构确实会使第二个断言失败TestAsync- 正如我所期望的那样.

我的问题是这个 - 除了不使用async/await的可变结构之外是否有一种优雅的方式使它们和平共存?

Ser*_*rvy 10

对于一种变异"自身" 的async方法来说,本质上是不可能的struct.

当你想到它时,这当然是完全合理的.当你await在该结构中的任何任务实际完成时,假定你已经返回到调用者并允许他们继续做各种事情,你无法确保调用该方法的实际struct实例甚至还存在.如果SetValueAsync通过一个没有await它的方法或Wait它上面的那个或类似的方法调用一个局部变量那么那个局部变量的生命周期可能会在SetValueAsync到达它的调用的延续时间结束Run.它不能改变生命周期可能在也可能不在范围内的变量.这里唯一的选择是async结构的方法在调用方法时有效地复制自己,并使延续引用中的代码成为调用该变量的变量的完全不同的变量async.由于该方法正在制作一个副本,除了这个async方法的主体以外的任何地方都无法访问,这意味着,对于所有意图的目的,async结构的方法永远不能改变该结构(并且通过还有谁).

你可以有async一个可变的方法struct,只要该方法本身不会改变struct.那个方法需要返回一个Task<T>带有新结构或等价的东西.

作为一个有趣的方法,如果它真的想要在方法的第一个async方法之前struct改变自己的方法,那么它在技​​术可能性的范围内.编译器选择立即获取副本,因此这实际上是不可能的,但是明确的选择是在方法的最开始而不是仅在第一个之后制作副本.这可能是最好的,无论是否是故意的决定,否则将是超级混乱.awaitawait