简体   繁体   English

C#:实现异步接口的同步方法的约定

[英]C#: convention for synchronous method implementing an async interface

I have an interface that needs to be asynchronous.我有一个需要异步的接口。 Most of the implementers of that interface will be asynchronous methods.该接口的大多数实现者都是异步方法。

interface IValidator {
  Task<bool> ValidateAsync();
}

However, one implementation of it does not make any async calls, and I'm curious what the convention for implementing that particular method is.但是,它的一种实现不会进行任何异步调用,我很好奇实现该特定方法的约定是什么。 The way I see it, there are 2 options:在我看来,有两种选择:

public async Task<bool> ValidateAsync() {
  return await Task.FromResult(SomeOtherMethod());
}

or或者

public Task<bool> ValidateAsync() {
  return Task.FromResult(SomeOtherMethod());
}

I'm curious what the proper convention is.我很好奇正确的约定是什么。 Or is there some other option that I should use?还是我应该使用其他选项?

To return a task, you can either create one yourself ( Task.FromResult ) or have the compiler create one for you ( async ).要返回任务,您可以自己创建一个( Task.FromResult )或让编译器为您创建一个( async )。

Since there are no await s, some people choose to create it themselves.由于没有await ,所以有些人选择自己创建。 However (as noted in the comments), this causes unexpected behavior with regards to exceptions.但是(如评论中所述),这会导致有关异常的意外行为。 The implicit assumption for a task-returning method is that any exceptions from that method will be returned on that task, not thrown directly.任务返回方法的隐含假设是该方法的任何异常都将在该任务上返回,而不是直接抛出。

So, this code I would say is not ideal, since it raises exceptions directly to the caller:所以,我想说的这段代码并不理想,因为它直接向调用者引发异常:

public Task<bool> ValidateAsync() {
  // Any exceptions from SomeOtherMethod are propagated directly.
  return Task.FromResult(SomeOtherMethod());
}

A more correct build-your-own-task solution would look like this:更正确的 build-your-own-task 解决方案如下所示:

public Task<bool> ValidateAsync() {
  try {
    return Task.FromResult(SomeOtherMethod());
  }
  catch (Exception ex) {
    return Task.FromException<bool>(ex);
  }
}

And you may wish to make that more complex if you want to handle OperationCanceledException specially by returning a cancelled task ( Task.FromCanceled ).如果您想通过返回已取消的任务 ( Task.FromCanceled ) 来专门处理OperationCanceledException ,您可能希望使其更复杂。 That step isn't usually necessary but some people prefer it (or need it, depending on how the rest of their code handles cancellation).该步骤通常不是必需的,但有些人更喜欢它(或需要它,这取决于他们的代码的 rest 如何处理取消)。

So, this pattern is already looking a bit complex, especially if you're repeating it.所以,这个模式看起来已经有点复杂了,尤其是在你重复它的时候。

The other option is to use async to create the task for you.另一种选择是使用async您创建任务。 And, while you can do this with await Task.FromResult , that's really just adding overhead compared to this:而且,虽然您可以使用await Task.FromResult执行此操作,但与此相比,这实际上只是增加了开销:

public async Task<bool> ValidateAsync() {
  return SomeOtherMethod();
}

This solution is good in that it properly handles exceptions/cancellation (ie, all control flows).这个解决方案很好,因为它可以正确处理异常/取消(即所有控制流)。 This solution has a problem, though: it causes a CS1998 compiler warning.但是,此解决方案有一个问题:它会导致 CS1998 编译器警告。 To be clear, the compiler warning is a good thing, because a missing await is usually a mistake, and the warning is very clear about how the method will run synchronously.需要明确的是,编译器警告是一件好事,因为缺少await通常是一个错误,并且警告非常清楚该方法将如何同步运行。 Which in this case is what we want.在这种情况下,这就是我们想要的。

So, my preferred solution is to just do the above with the compiler warning disabled.所以,我首选的解决方案是在禁用编译器警告的情况下执行上述操作。 If this is a pattern you plan to reuse several times in your code, you can add a trivial helper method like this so that the #pragma only happens in one place:如果这是您计划在代码中多次重用的模式,您可以添加一个像这样的简单辅助方法,以便#pragma只在一个地方发生:

public static class TaskHelper
{
#pragma warning disable 1998
  public static async Task ExecuteAsTask(Action func)
#pragma warning restore 1998
  {
    _ = func ?? throw new ArgumentNullException(nameof(func));
    func();
  }

#pragma warning disable 1998
  public static async Task<T> ExecuteAsTask<T>(Func<T> func)
#pragma warning restore 1998
  {
    _ = func ?? throw new ArgumentNullException(nameof(func));
    return func();
  }
}

Usage:用法:

public Task<bool> ValidateAsync() {
  return TaskHelpers.ExecuteAsTask(() => SomeOtherMethod());
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM