C# 使用异步/等待的最佳实践

C# 使用异步/等待的最佳实践,c#,async-await,C#,Async Await,假设我有以下类定义: public class Calculator { public CalculatorResult Calculate() { return LongRunningCalculation(); } private CalculatorResult LongRunningCalculation() { return new CalculatorResult(0.00); } } public

假设我有以下类定义:

public class Calculator
{
    public CalculatorResult Calculate()
    {
        return LongRunningCalculation();
    }

    private CalculatorResult LongRunningCalculation()
    {
        return new CalculatorResult(0.00);
    }
}

public class ClassThatUsesACalculator
{
    private readonly Calculator calculator;

    public ClassThatUsesACalculator()
    {
        this.calculator = new Calculator();
    }

    public void DoWork()
    {
        for (int i = 0; i < 10; i++)
        {
            var result = calculator.Calculate();

            DoSomethingWithCalculationResult(result);

            DoLightWork();

            OnProgressChanged();
        }
    }
}

public partial class Form : Form
{
    public Form()
    {
        InitializeComponent();
    }

    private void Method(object sender, EventArgs e)
    {
        DoWork();
    }

    private void DoWork()
    {
        var calculator = new ClassThatUsesACalculator();
        calculator.ProgressChanged += (s, e) =>
        {
            // Update progressbar
        };

        calculator.DoWork();
    }
}
如果我拥有这个库,我想还有两个选择,其中一个非常像第一个。可能是因为我自己不了解

在使用计算器的
类上创建一个方法,该方法封装
DoWork()
方法,然后从窗体上的异步方法调用该方法

或者

  • 使用
    Task.Run()
    封装
    Calculator
    类上的
    LongRunningCalculation()

  • 在表单上创建异步方法(
    MethodThree

    private async void method三(对象发送方,事件参数e)
    {
    IProgress progress=新进度(UpdateProgressBar);
    var calculator=使用计算器()的新类;
    calculator.ProgressChanged+=(s,args)=>
    {
    进度报告(新计算器进度(0));
    };
    等待计算器。DoWorkAsync();
    }
    

  • 现在,在我看来,最后一个选择将是最好的,因为我将保持更多的控制权。但是,也许我离此很远,希望有人对此发表意见或指点,因为我只能找到关于如何使用异步的解释,而不能找到真正的方法来为其他人使用。

    一般来说,推送任何
    任务。尽可能在调用堆栈上运行
    用法

    您想要避免的是使用
    Task.Run
    在可重用组件中实现具有异步签名的方法。那是个骗人的API。我有一个更详细的计划

    如果您控制有问题的类,我建议使用
    IProgress
    而不是事件进行进度更新
    IProgress
    可以很好地处理同步代码和异步代码:

    public void DoWork(IProgress<CalculatorProgress> progress = null)
    {
      for (int i = 0; i < 10; i++)
      {
        var result = calculator.Calculate();
    
        DoSomethingWithCalculationResult(result);
    
        DoLightWork();
    
        if (progress != null)
          progress.Report(new CalculatorProgress(...));
      }
    }
    
    public void DoWork(IProgress progress=null)
    {
    对于(int i=0;i<10;i++)
    {
    var result=calculator.Calculate();
    计算结果(结果)的dosomething;
    DoLightWork();
    如果(进度!=null)
    进度报告(新计算器程序(…);
    }
    }
    
    那么使用它是非常简单的:

    private async void MethodTwo(object sender, EventArgs e)
    {
      IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar);
    
      var calculator = new ClassThatUsesACalculator();
    
      await Task.Run(() => calculator.DoWork(progress));
    }
    
    private async void method2(对象发送方,事件参数e)
    {
    IProgress progress=新进度(UpdateProgressBar);
    var calculator=使用计算器()的新类;
    等待任务。运行(()=>calculator.DoWork(progress));
    }
    

    这就保留了
    任务。在需要它的组件(UI层)中运行
    用法,并将其排除在业务逻辑之外。

    您的问题非常长,而且过于冗长。如果您将问题缩小到简要地解释您想知道的内容,可能会有所帮助。顺便说一句,对于快速的东西,
    async
    实际上并不是必需的<代码>异步
    对于访问文件、数据库或web服务等I/O任务非常有用。但是运行快速非I/O代码的开销实际上可能会降低性能。这应该在山顶上演唱。
    public async Task DoWorkAsync()
    {
        for (int i = 0; i < 10; i++)
        {
            var result = await calculator.CalculateAsync();
    
            DoSomethingWithCalculationResult(result);
    
            DoLightWork();
    
            OnProgressChanged(); 
        }
    }
    
    private async void MethodThree(object sender, EventArgs e)
    {
        IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar);
    
        var calculator = new ClassThatUsesACalculator();
        calculator.ProgressChanged += (s, args) =>
        {
            progress.Report(new CalculatorProgress(0));
        };
    
        await calculator.DoWorkAsync();
    }
    
    public void DoWork(IProgress<CalculatorProgress> progress = null)
    {
      for (int i = 0; i < 10; i++)
      {
        var result = calculator.Calculate();
    
        DoSomethingWithCalculationResult(result);
    
        DoLightWork();
    
        if (progress != null)
          progress.Report(new CalculatorProgress(...));
      }
    }
    
    private async void MethodTwo(object sender, EventArgs e)
    {
      IProgress<CalculatorProgress> progress = new Progress<CalculatorProgress>(UpdateProgressBar);
    
      var calculator = new ClassThatUsesACalculator();
    
      await Task.Run(() => calculator.DoWork(progress));
    }