组合等待与 ContinueWith

Combination await with ContinueWith

我遇到了以下我正在尝试理解的异步代码(针对此示例进行了简化):

class Program
{
    static async Task Main(string[] args)
    {
        var foo = new Foo();

        try
        {
            var bar = new Bar();
            await foo.ComputeNumber(bar.GetNumberAsync());
        }
        catch (Exception e)
        {
            Console.WriteLine($"Logged exception: {e}");
        }
        Console.WriteLine($"The number is {foo.Number}");
        Console.ReadKey();
    }
}

public class Foo
{
    public int Number { get; private set; } = 0;

    public async Task ComputeNumber(Task<int> inputTask)
    {
        await inputTask.ContinueWith(x => Number = x.Result);
    }
}

public class Bar
{
    public async Task<int> GetNumberAsync()
    {
        return await Task.Factory.StartNew(() =>
        {
            if (DateTime.Now.Date.DayOfWeek != DayOfWeek.Monday)
            {
                throw new Exception("This function works only on Mondays.");
            }
            return 17;
        });
    }
}

这段代码按我预期的方式工作(至少我希望如此),但我认为这个问题应该通过以下方式之一解决(我认为两者都是正确的)。 Bar class 将保持不变。

第一种(async/await)方法:

class Program
{
    static async Task Main(string[] args)
    {
        var foo = new Foo();

        try
        {
            var bar = new Bar();
            await foo.ComputeNumber(bar.GetNumberAsync());
        }
        catch (Exception e)
        {
            Console.WriteLine($"Logged exception: {e}");
        }
        Console.WriteLine($"The number is {foo.Number}");
        Console.ReadKey();
    }
}

public class Foo
{
    public int Number { get; private set; } = 0;

    public async Task ComputeNumber(Task<int> inputTask)
    {
        Number = await inputTask;
    }
}

第二种(基于任务的)方法:

class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();
        var bar = new Bar();
        foo.ComputeNumber(bar.GetNumberAsync());

        Console.WriteLine($"The number is {foo.Number}");
        Console.ReadKey();
    }
}

public class Foo
{
    public int Number { get; private set; } = 0;

    public void ComputeNumber(Task<int> inputTask)
    {
        inputTask.ContinueWith(x =>
        {
            if (x.IsFaulted)
            {
                Console.WriteLine($"Logged exception: {x.Exception}");
            }
            else
            {
                Number = x.Result;
            }
        });
    }
}

对于为什么可以这样编写使用异步代码的原始示例的每一种解释,我都很感激。

您可以将 await 视为替代 ContinueWith 大部分用法的语言功能,因此将两者混合似乎没有必要。

请注意,您的第二次重写与第一次重写在两个方面不同。

  1. 它吞下了错误而不是暴露它们。

  2. 它没有 return 和 Task 所以调用者不知道什么时候 Number 被设置为最终结果。

ComputeNumber 的目的不明(这可能是因为为了在此处发布而对其进行了简化)。为什么不简单地 await bar.GetNumberAsync() 然后在后续语句中使用获得的值? await 的要点是允许 Task<T> 在程序代码中被视为与 T 相同。