为什么需要在非异步方法中包装参数验证?

Why is wrapping parameter validation in a non-async method desirable?

我在 SonarQube 中弹出 this 警告,但我不明白为什么需要包装验证。

我已阅读以下问题,但似乎都没有清楚地解释为什么它更好?

Validate parameters in async method

在例子中

public async Task DoSomethingAsync(string param){
    if(string.IsNullOrEmpty(param)
    {
        throw new ArgumentException("Param is blank");
    }

    await DoSomethingElseAsync(param);
}

为什么这会以不同的方式执行

public Task DoSomethingAsync(string param){
    if(string.IsNullOrEmpty(param)
    {
        throw new ArgumentException("Param is blank");
    }

    return doSomethingElseAsync(param);
}

鉴于调用者需要等待两个实现,是否真的可以保证第二个实现中的验证将立即执行而不是像任何其他异步方法一样被延迟?

编译器将异步方法转换为状态机。

当方法被调用时,状态机被实例化。只有这样,第一个 await 之前的代码才会被调用。

如果你有这个方法:

private static async Task<int> GetValueAsync(object o)
{
    if (o is null) throw new ArgumentNullException(nameof(o));
    await Task.Delay(1);
    return 0;
}

生成的代码将是:

[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <GetValueAsync>d__0 : IAsyncStateMachine
{
    public int <>1__state;

    public AsyncTaskMethodBuilder<int> <>t__builder;

    public object o;

    private TaskAwaiter <>u__1;

    private void MoveNext()
    {
        int num = <>1__state;
        int result;
        try
        {
            TaskAwaiter awaiter;
            if (num != 0)
            {
                if (o == null)
                {
                    throw new ArgumentNullException("o");
                }
                awaiter = Task.Delay(1).GetAwaiter();
                if (!awaiter.IsCompleted)
                {
                    num = (<>1__state = 0);
                    <>u__1 = awaiter;
                    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                    return;
                }
            }
            else
            {
                awaiter = <>u__1;
                <>u__1 = default(TaskAwaiter);
                num = (<>1__state = -1);
            }
            awaiter.GetResult();
            result = 0;
        }
        catch (Exception exception)
        {
            <>1__state = -2;
            <>t__builder.SetException(exception);
            return;
        }
        <>1__state = -2;
        <>t__builder.SetResult(result);
    }

    void IAsyncStateMachine.MoveNext()
    {
        //ILSpy generated this explicit interface implementation from .override directive in MoveNext
        this.MoveNext();
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    {
        <>t__builder.SetStateMachine(stateMachine);
    }

    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    {
        //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
        this.SetStateMachine(stateMachine);
    }
}

[AsyncStateMachine(typeof(<GetValueAsync>d__0))]
private static Task<int> GetValueAsync(object o)
{
    <GetValueAsync>d__0 stateMachine = default(<GetValueAsync>d__0);
    stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
    stateMachine.o = o;
    stateMachine.<>1__state = -1;
    stateMachine.<>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}

如果您在非异步方法中验证参数:

private static Task<int> GetValueAsync(object o)
{
    if (o is null) throw new ArgumentNullException(nameof(o));
    return GetValueAsyncImpl(o);

    static async Task<int> GetValueAsyncImpl(object o)
    {
        await Task.Delay(1);
        return 0;
    }
}

生成的代码将是:

[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <<GetValueAsync>g__GetValueAsyncImpl|0_0>d : IAsyncStateMachine
{
    public int <>1__state;

    public AsyncTaskMethodBuilder<int> <>t__builder;

    private TaskAwaiter <>u__1;

    private void MoveNext()
    {
        int num = <>1__state;
        int result;
        try
        {
            TaskAwaiter awaiter;
            if (num != 0)
            {
                awaiter = Task.Delay(1).GetAwaiter();
                if (!awaiter.IsCompleted)
                {
                    num = (<>1__state = 0);
                    <>u__1 = awaiter;
                    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref this);
                    return;
                }
            }
            else
            {
                awaiter = <>u__1;
                <>u__1 = default(TaskAwaiter);
                num = (<>1__state = -1);
            }
            awaiter.GetResult();
            result = 0;
        }
        catch (Exception exception)
        {
            <>1__state = -2;
            <>t__builder.SetException(exception);
            return;
        }
        <>1__state = -2;
        <>t__builder.SetResult(result);
    }

    void IAsyncStateMachine.MoveNext()
    {
        //ILSpy generated this explicit interface implementation from .override directive in MoveNext
        this.MoveNext();
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    {
        <>t__builder.SetStateMachine(stateMachine);
    }

    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    {
        //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
        this.SetStateMachine(stateMachine);
    }
}

private static Task<int> GetValueAsync(object o)
{
    if (o == null)
    {
        throw new ArgumentNullException("o");
    }
    return <GetValueAsync>g__GetValueAsyncImpl|0_0(o);
}

[AsyncStateMachine(typeof(<<GetValueAsync>g__GetValueAsyncImpl|0_0>d))]
[CompilerGenerated]
internal static Task<int> <GetValueAsync>g__GetValueAsyncImpl|0_0(object o)
{
    <<GetValueAsync>g__GetValueAsyncImpl|0_0>d stateMachine = default(<<GetValueAsync>g__GetValueAsyncImpl|0_0>d);
    stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
    stateMachine.<>1__state = -1;
    stateMachine.<>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}

如果参数值无效,则不会实例化状态机。

迭代器方法也是如此。

假设调用者会依次调用和await异步方法,没有区别。

await DoSomethingAsync("Chess");
await DoSomethingAsync("Stratego");
await DoSomethingAsync(null);

但是你怎么能确定呢?调用者也可以将创建任务的阶段和等待任务的阶段分开。

var tasks = new List<Task>();
tasks.Add(DoSomethingAsync("Chess"));
tasks.Add(DoSomethingAsync("Stratego"));
tasks.Add(DoSomethingAsync(null));
await Task.WhenAll(tasks);

在这种情况下,两种不同的 DoSomethingAsync 实现将导致调用者代码中出现不同的行为。

另一个区别是 XML documentation of the method, assuming that you want to be consistent with Microsoft's guidelines. The first implementation requires no <exception> 元素,因为不能同步抛出异常:

/// <summary>Does something asynchronously.</summary>
/// <param name="param">The parameter.</param>
/// <returns>A <see cref="Task"/> that will complete when something is done.</returns>
public async Task DoSomethingAsync(string param)
{
    // ...
}

第二个实现确实需要一个 <exception> 元素,因为 ArgumentException 可以同步抛出:

/// <summary>Does something asynchronously.</summary>
/// <param name="param">The parameter.</param>
/// <returns>A <see cref="Task"/> that will complete when something is done.</returns>
/// <exception cref="ArgumentException"><paramref name="param"/> is blank.</exception>
public Task DoSomethingAsync(string param)
{
    // ...
}