验证异步方法中的参数

Dem*_*sch 11 c# asynchronous

我正在写有相同方法的同步和异步版本类 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)

Mar*_*ell 8

这取决于你何时想要提出错误 - 即急切地,或作为等待的一部分.与迭代器块一样,如果要进行急切的错误检查,则需要两种方法,例如:

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状态的状态.


Sem*_*nko 8

从 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)


Hir*_*ana 5

根据声纳规则 S4457

由于编译器重写 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)

  • SonarQube 就是这么说的。(这是你写的吗?)我仍然不明白为什么合规解决方案更好。编译器仅为等待的部分生成一个单独的方法,因此我的堆栈跟踪将显示与我的源代码完美对应的错误。(我刚刚测试过这个) (5认同)