带和不带异步的任务返回类型[重复]

问题描述 投票:0回答:2

我对

async
关键字的行为有点困惑。

假设我有两种方法,

public async Task DoSomething1()
{
    await Task.Run(() =>
    {
        for(int i = 0; i<3; i++)
        {
            Console.WriteLine("I m doing something:" + i);
        }
    });
}

还有

public Task DoSomething2()
{
    return Task.Run(() =>
    {
        for(int i = 0; i<3; i++)
        {
            Console.WriteLine("I m doing something:" + i);
        }
    });
}

据我了解,这两种方法都是值得期待的。但是,当我编写一个具有

Task
返回类型但没有
async
关键字的方法时,我需要返回
Task
,否则编译器会生成错误。方法应该返回其类型,这是很常见的事情。但是当我将它与
async
关键字一起使用时,编译器会生成另一个错误,您无法返回
Task
。那么这意味着什么呢?我在这里完全困惑了。

c# .net asynchronous async-await task-parallel-library
2个回答
12
投票

一般异步信息

如果您在代码中使用

await
,则需要在方法中使用
async
关键字。

如果您使用

async
并希望返回实际类型,您可以声明您的方法将该类型返回为通用任务,如下所示
Task<int>

以下是

async
方法的有效返回类型:

https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/async-return-types

  1. Task<TResult>
    ,用于返回值的异步方法。
  2. Task
    ,用于执行操作但不返回任何值的异步方法。
  3. void
    ,用于事件处理程序。

2022 年 8 月的新答案

TL;DR - 返回类型自动包装在任务中。

我对此投了反对票,所以我更详细地重新阅读了这个问题并找到了问题的根源。这与方法签名中的返回类型无关。它是关于返回值;为什么我不需要返回显式的

Task
类型? (而且这不是重复的问题!)

不管怎样,我们来看看这个方法:

public Task TaskMethod() 
{
    return Task.CompletedTask;        
}

这看起来很正常,也是我们在 C# 中所习惯的。您声明返回类型并返回该类型的对象。简单的。 (事实上,“降低”的代码是完全相同的。)

现在介绍异步情况。

public async Task MethodAsync() 
{
    return Task.CompletedTask;  
}

这会生成编译错误:

error CS1997: Since 'C.MethodAsync()' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?

好吧,那我们就不能直接返回任务了,我们来做正确的事情吧。如果我们返回一些东西,就更容易看到这个例子,所以让我们返回 int。

public async Task<int> MethodAsync() 
{
    return 1;
}

(此方法会发出警告,表明我们正在使用异步而不等待,但现在忽略它。)

降低的代码复杂,但是可以看到会生成一个状态机。 MethodAsync 看起来像这样:

[CompilerGenerated]
private sealed class <MethodAsync>d__0 : IAsyncStateMachine
{
    public int <>1__state;

    public AsyncTaskMethodBuilder<int> <>t__builder;

    public C <>4__this;

    private void MoveNext()
    {
        int num = <>1__state;
        int result;
        try
        {
            result = 1;
        }
        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)
    {
    }

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

[AsyncStateMachine(typeof(<MethodAsync>d__0))]
[DebuggerStepThrough]
public Task<int> MethodAsync()
{
    <MethodAsync>d__0 stateMachine = new <MethodAsync>d__0();
    stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
    stateMachine.<>4__this = this;
    stateMachine.<>1__state = -1;
    stateMachine.<>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}

并且可以看到这里的Task是在public方法中自动返回的。状态机实现方法体。

您可以看到返回的实际值是包装在任务中的

<>t__builder
的 int。

要亲自查看降低的代码,您可以在https://sharplab.io中尝试。

写完所有这些之后,我找到了另一个答案,以不同的方式解释了它。猜猜这个答案是谁写的? https://stackoverflow.com/a/37647093/1804678


2
投票

(...)“异步”。这个关键词 允许在方法内部使用“await”一词,并且 修改方法处理其结果的方式。就是这样。

该方法同步运行,直到找到单词“await”并且该 word 是负责异步的。 “Await”是一个运算符 接收参数:可等待(异步操作) 行为方式如下:

这里

© www.soinside.com 2019 - 2024. All rights reserved.