C# 异步方法,无等待vs.Task.FromResult

C# 异步方法,无等待vs.Task.FromResult,c#,asynchronous,async-await,C#,Asynchronous,Async Await,考虑以下接口: public interface IProvider { Task<bool> Contains(string key); } 公共接口IProvider { 任务包含(字符串键); } 这是VisualStudio的实现 public Task<bool> Contains(string key) { return Task.FromResult(false); } 公共任务包含(字符串键) { 返回Task.FromResult(

考虑以下接口:

public interface IProvider
{
    Task<bool> Contains(string key);
}
公共接口IProvider
{
任务包含(字符串键);
}
这是VisualStudio的实现

public Task<bool> Contains(string key)
{
    return Task.FromResult(false);
}
公共任务包含(字符串键)
{
返回Task.FromResult(false);
}
此实现便于编写,并且似乎实现了相同的功能:

public async Task<bool> Contains(string key)
{
    return false;
}
公共异步任务包含(字符串键)
{
返回false;
}
然而,Visual Studio发出嘶嘶声并坚持:

此异步方法缺少“await”运算符,将同步运行。考虑使用“Acess”操作符等待非阻塞API调用,或“等待TaskEX.Run(…)”在后台线程上执行CPU绑定的工作。 我希望忽略这个警告,避免使用
Task.FromResult(…)

使用后一个选项会有任何负面影响吗?

之所以会出现“hissy-fit”,是因为编译器需要做大量的工作来呈现一个以所有预期正确方式工作的任务,您可以在这里看到

Task.FromResult
更干净,但可能仍有开销-IIRC在某些情况下,
Task.FromResult
可能在这里有效工作(每次返回相同的对象),但我不会依赖它

有两种实用可靠的方法:

  • 每次返回重复使用的静态
    任务
    结果
  • 使用
    ValueTask
    ——如果您经常同步返回,这在这里似乎很理想
i、 e

private readonly static Task s_False=Task.FromResult(False);
公共任务包含(字符串键、字符串范围)
{
返回s_False;
}

public ValueTask包含(字符串键、字符串范围)
{
返回新的ValueTask(false);
}
注意:在这种情况下,第二个可能不可能,因为您没有定义接口。但是,如果你正在设计一个接口,它需要允许异步使用,但实际上可能是同步的:考虑使用<代码> ValueTask作为交换类型,而不是<代码>任务< /> > 生成的C#为:

public async System.Threading.Tasks.Task包含(字符串键、字符串范围)
{
返回false;
}
有点像:

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

    public AsyncTaskMethodBuilder<bool> <>t__builder;

    private void MoveNext()
    {
        bool result;
        try
        {
            result = false;
        }
        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(<Contains>d__0))]
public Task<bool> Contains(string key, string scope)
{
    <Contains>d__0 stateMachine = default(<Contains>d__0);
    stateMachine.<>t__builder = AsyncTaskMethodBuilder<bool>.Create();
    stateMachine.<>1__state = -1;
    AsyncTaskMethodBuilder<bool> <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}
[StructLayout(LayoutKind.Auto)]
[编译生成]
私有结构d__0:IAsyncStateMachine
{
州内公共交通;
公共异步TaskMethodBuilder t_uuBuilder;
私有void MoveNext()
{
布尔结果;
尝试
{
结果=假;
}
捕获(异常)
{
1_uu状态=-2;
设置异常(异常);
返回;
}
1_uu状态=-2;
设置结果(结果);
}
void IAsyncStateMachine.MoveNext()
{
//ILSpy根据MoveNext中的.override指令生成此显式接口实现
this.MoveNext();
}
[调试程序隐藏]
私有void SetStateMachine(IAsyncStateMachine stateMachine)
{
t_ubuilder.SetStateMachine(stateMachine);
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine状态机)
{
//ILSpy根据SetStateMachine中的.override指令生成此显式接口实现
此.SetStateMachine(stateMachine);
}
}
[AsyncStateMachine(typeof(d_u0))]
公共任务包含(字符串键、字符串范围)
{
d_u0状态机=默认值(d_u0);
stateMachine.t__builder=AsyncTaskMethodBuilder.Create();
stateMachine.1_uu状态=-1;
AsyncTaskMethodBuilder t\uuuu builder=stateMachine.t\uu builder;
启动(参考状态机);
返回stateMachine.t\u builder.Task;
}

为什么?使用
Task.FromResult()
是正确的调用,因为您的代码不会异步执行任何操作
async
不会使某些东西异步运行,它只是语法糖,允许您等待已经异步的操作。使用
async
仅仅返回
false
并不方便,它比简单地返回
任务
实例更冗长。顺便问一下,这段代码有什么意义?如果不打算异步运行任何东西,为什么要使用任务?为什么不编写一个
公共bool Contains(..)
?您的第一个实现只返回一个
任务,并且不执行异步操作。
。您的第二个实现被标记为
async
,并且您没有执行任何异步运行任务。你当然会得到警告。顺便说一句:您的接口和方法有不同的参数计数。你确定你实现了这个接口吗?@PanagiotisKanavos:我想这就是
任务的用例。FromResult
。当接口允许异步调用者,但实现是同步的。@PanagiotisKanavos-如果其他人定义了
IProvider
,并且您正在尝试实现它,您将没有太多选择。感谢您的回答,特别是对一段显然很简单的代码背后发生的事情的深入了解。有时很容易忘记在CLR深处到底发生了多少事情。:)
public ValueTask<bool> Contains(string key, string scope)
{
    return new ValueTask<bool>(false);
}
public async System.Threading.Tasks.Task<bool> Contains(string key, string scope)
{
    return false;
}
[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <Contains>d__0 : IAsyncStateMachine
{
    public int <>1__state;

    public AsyncTaskMethodBuilder<bool> <>t__builder;

    private void MoveNext()
    {
        bool result;
        try
        {
            result = false;
        }
        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(<Contains>d__0))]
public Task<bool> Contains(string key, string scope)
{
    <Contains>d__0 stateMachine = default(<Contains>d__0);
    stateMachine.<>t__builder = AsyncTaskMethodBuilder<bool>.Create();
    stateMachine.<>1__state = -1;
    AsyncTaskMethodBuilder<bool> <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}