验证异步方法中的参数

时间:2013-09-06 11:09:18

标签: c# asynchronous

我正在编写一个具有相同方法的同步和异步版本的类 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"); });

4 个答案:

答案 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)

按照sonar rule S4457

  

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