C#

structとTaskでハマった話

More than 1 year has passed since last update.


問題

以下のコード、何が表示されると思いますか?

struct S

{
public int Value;
public S(int init) => Value = init;
public async Task F()
{
Value++;
await Task.CompletedTask;
}
}

public static async Task Main()
{
var s = new S(10);
await s.F();
Console.WriteLine(s.Value.ToString());
}

答えは 10 です。

では、以下のコード、何が表示されると思いますか?

struct S

{
public int Value;
public S(int init) => Value = init;
public Task F()
{
Value++;
return Task.CompletedTask;
}
}

public static async Task Main()
{
var s = new S(10);
await s.F();
Console.WriteLine(s.Value.ToString());
}

答えは 11 です。

どうしてこのようになるのでしょうか?

答えはこちら


解説

上記2つのコードの違いは S.F() がasyncメソッドかどうかという点です。

どうしてこうなるか、というと、asyncメソッドの場合Awaiterというものが作成されます。

これは、デリゲートの場合でいうファンクタオブジェクトのようなものです。

このAwaiterに構造体Sがコピーされてしまうため、S.F()が呼ばれるインスタンスはAwaiter.sであり、ローカル変数のsではないことが原因です。


解決策


  • Sをclassにする

  • F()の返り値をTask<S>にして、return thisしたうえで呼び出しをs = await s.F()にする