我正在写有相同方法的同步和异步版本类
void MyMethod(object argument)和Task MyMethodAsync(object argument).在同步版本中,我通过简单检查验证参数
if (argument == null)
throw new ArgumentNullException("argument");
Run Code Online (Sandbox Code Playgroud)
在异步方法中,相同的检查应该如何?
1)与同步方法相同
2)(第一次回答后更新)
if (argument == null)
return new Task.Factory.StartNew(() => { throw new ArgumentNullException("argument"); });
Run Code Online (Sandbox Code Playgroud)
这取决于你何时想要提出错误 - 即急切地,或作为等待的一部分.与迭代器块一样,如果要进行急切的错误检查,则需要两种方法,例如:
public Task<int> SomeMethod(..args..) {
if(..args fail..) throw new InvalidOperationException(...);
return SomeMethodImpl(...args...);
}
private async Task<int> SomeMethodImpl(...args...)
{
... await etc ...
}
Run Code Online (Sandbox Code Playgroud)
然后,这将作为初始调用的一部分执行任何参数检查,而不是等待.如果您希望异常成为等待的一部分,您可以抛出它:
public async Task<int> SomeMethod(..args..) {
if(..args fail..) throw new InvalidOperationException(...);
... await etc ...
}
Run Code Online (Sandbox Code Playgroud)
然而,在你的榜样,你是这样的事实return荷兰国际集团一Task认为,这实际上不是一个async方法,但是是一个异步的(但不是async)方法.你不能只做:
return new Task(() => { throw new ArgumentNullException("argument"); });
Run Code Online (Sandbox Code Playgroud)
因为那Task永远不会开始 - 永远不会.我怀疑你需要做以下事情:
try {
throw new InvalidArgumentException(...); // need to throw to get stacktrace
} catch(Exception ex) {
var source = new TaskCompletionSource<int>();
source.SetException(ex);
return source.Task;
}
Run Code Online (Sandbox Code Playgroud)
这是......有点拗口,可能会被封装好一点.这将返回一个Task表示它处于Faulted状态的状态.
从 C# 7.0 开始,您可以使用局部函数来减少代码中的噪音,但仍然符合声纳规则 S4457 中的参数检查实践。例如,此代码在两种情况下都会抛出 ArgumentNullException:如果您使用 await 或不使用 await 调用它。
private Task WaitSeconds(int? durationInSeconds)
{
if(durationInSeconds == null) throw new ArgumentNullException(nameof(durationInSeconds));
async Task WaitSecondsInternal()
{
await Task.Delay(TimeSpan.FromSeconds(durationInSeconds.Value));
}
return WaitSecondsInternal();
}
Run Code Online (Sandbox Code Playgroud)
由于编译器重写 async/await 方法的方式,参数检查期间抛出的任何异常只会在观察到任务时发生。这可能发生在远离错误代码源的地方,或者永远不会发生在即发即忘的任务中。
因此,建议将该方法拆分为两个:一个处理参数检查的外部方法(没有 async/await)和一个处理具有 async/await 模式的迭代器块的内部方法。
当异步方法抛出从 ArgumentException 派生的任何异常并包含 await 关键字时,此规则会引发问题。
public static async Task SkipLinesAsync(this TextReader reader, int linesToSkip) // Noncompliant
{
if (reader == null) { throw new ArgumentNullException(nameof(reader)); }
if (linesToSkip < 0) { throw new ArgumentOutOfRangeException(nameof(linesToSkip)); }
for (var i = 0; i < linesToSkip; ++i)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null) { break; }
}
}
Run Code Online (Sandbox Code Playgroud)
public static Task SkipLinesAsync(this TextReader reader, int linesToSkip)
{
if (reader == null) { throw new ArgumentNullException(nameof(reader)); }
if (linesToSkip < 0) { throw new ArgumentOutOfRangeException(nameof(linesToSkip)); }
return reader.SkipLinesInternalAsync(linesToSkip);
}
private static async Task SkipLinesInternalAsync(this TextReader reader, int linesToSkip)
{
for (var i = 0; i < linesToSkip; ++i)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
if (line == null) { break; }
}
}
Run Code Online (Sandbox Code Playgroud)