我遇到了非常相似的问题/疑问。我试图实现在接口中指定的异步方法(例如public Task DoSomethingAsync())。换句话说,接口期望特定函数 (DoSomething) 是异步的。
然而,事实证明该实现可以同步完成(而且我认为它的方法也不会花费很长时间来完成)。
public interface IFoobar
{
Task DoSomethingAsync(Foo foo, Bar bar);
}
public class Caller
{
public async void Test
{
try
{
await new Implementation().DoSomethingAsync(null, null);
}
catch (Exception e)
{
Logger.Error(e);
}
}
}
现在有四种方法可以做到这一点。
方法一:
public class Implementation : IFoobar
{
public Task DoSomethingAsync(Foo foo, Bar bar)
{
if (foo == null)
throw new ArgumentNullException(nameof(foo));
if (bar == null)
throw new ArgumentNullException(nameof(bar));
DoSomethingWithFoobar(foo, bar);
}
}
方法二:
public class Implementation : IFoobar
{
#pragma warning disable 1998
public async Task DoSomethingAsync(Foo foo, Bar bar)
{
if (foo == null)
throw new ArgumentNullException(nameof(foo));
if (bar == null)
throw new ArgumentNullException(nameof(bar));
DoSomethingWithFoobar(foo, bar);
}
#pragma warning restore 1998
}
方法三:
public class Implementation : IFoobar
{
public Task DoSomethingAsync(Foo foo, Bar bar)
{
if (foo == null)
return Task.FromException(new ArgumentNullException(nameof(foo)));
if (bar == null)
return Task.FromException(new ArgumentNullException(nameof(bar)));
DoSomethingWithFoobar(foo, bar);
return Task.CompletedTask;
}
}
方法四:
public class Implementation : IFoobar
{
public Task DoSomethingAsync(Foo foo, Bar bar)
{
try
{
if (foo == null)
throw new ArgumentNullException(nameof(foo));
if (bar == null)
throw new ArgumentNullException(nameof(bar));
}
catch (Exception e)
{
return Task.FromException(e);
}
DoSomethingWithFoobar(foo, bar);
return Task.CompletedTask;
}
}
就像 Stephen Cleary 提到的那样,所有这些通常都有效。不过,还是有一些区别的。
- 方法 1 要求您同步捕获异常(在调用方法时而不是在等待时)。如果您使用延续 (
task.ContinueWith(task => {})) 来处理异常,延续将根本不会运行。这与您问题中的示例类似。
- 方法 2 实际上效果很好,但您将不得不接受警告或插入
#pragma 抑制。该方法最终可能会异步运行,从而导致不必要的上下文切换。
- 方法 3 似乎是最直观的。不过有一个副作用——stacktrace 根本不显示
DoSomethingAsync()!你所能看到的只是来电者。这可能会很糟糕,具体取决于您抛出了多少相同类型的异常。
- 方法4和方法2类似,可以
await+catch例外;你可以做任务延续;堆栈跟踪没有丢失的信息。它也是同步运行的,这对于非常轻量/快速的方法很有用。但是......对于你实现的每个方法都这样写是非常尴尬的。
请注意,我是从实现的角度讨论这个问题 - 我无法控制其他人如何调用我的方法。目的是以正常执行的方式实现,独立于调用方法。