我正在编写一个具有相同方法的同步和异步版本的类
void MyMethod(object argument)
和Task MyMethodAsync(object argument)
。在同步版本中,我使用简单检查验证参数
if (argument == null)
throw new ArgumentNullException("argument");
异步方法中的检查应该如何?
1)与同步方法相同
2)(第一次回答后更新)
if (argument == null)
return new Task.Factory.StartNew(() => { throw new ArgumentNullException("argument"); });
答案 0 :(得分:7)
这取决于你何时想要提出错误 - 即急切地,或作为等待的一部分。与迭代器块一样,如果您需要急切的错误检查,则需要两种方法,例如:
public Task<int> SomeMethod(..args..) {
if(..args fail..) throw new InvalidOperationException(...);
return SomeMethodImpl(...args...);
}
private async Task<int> SomeMethodImpl(...args...)
{
... await etc ...
}
这将执行任何参数检查作为初始调用的一部分,而不是等待的。如果您希望异常成为等待的一部分,您可以抛弃它:
public async Task<int> SomeMethod(..args..) {
if(..args fail..) throw new InvalidOperationException(...);
... await etc ...
}
但是,在您的示例中,您return
Task
的事实表明这实际上不是async
方法,而是 async (但不是async
)方法。你不能只做:
return new Task(() => { throw new ArgumentNullException("argument"); });
因为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;
}
这......有点拗口,可能会被封装好一点。这将返回Task
,表示它处于Faulted
状态。
答案 1 :(得分:4)
由于编译器会重写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; }
}
}
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; }
}
}
答案 2 :(得分:3)
简单地抛出它就像在sync方法中那样,TPL有各种机制来重新抛出异常,例如当你读取。Result
属性或访问。Exception
属性时
答案 3 :(得分:2)
从C#7.0开始,您可以使用local function来减少代码中的噪音,但仍符合sonar rule S4457中的参数检查惯例。 例如,在两种情况下,此代码都将引发ArgumentNullException:如果您在有等待的情况下调用它,或者在没有等待的情况下调用它。
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();
}