【问题标题】:How to write a Task wrapper type? (The return type of an async method must be a task-like type)如何编写任务包装器类型? (异步方法的返回类型必须是类任务类型)
【发布时间】:2021-06-30 22:20:22
【问题描述】:

考虑以下示例代码


    public static class TestClass
    { 
        static async TaskWrapper<int> Test()
        {
            var task = Task.FromResult(4);
            return await new TaskWrapper<int>(task);
        }
    }

    public class TaskWrapper<T>
    {
        private readonly Task<T> _thing;

        public TaskAwaiter<T> GetAwaiter() => _thing.GetAwaiter();

        public TaskWrapper(Task<T> thing)
        {
            _thing = thing;
        }
    }

编译器在Test()函数上报如下错误:error CS1983: The return type of an async method must be void, Task, Task&lt;T&gt;, a task-like type, IAsyncEnumerable&lt;T&gt;, or IAsyncEnumerator&lt;T&gt;

为什么会这样? TaskWrapper 类型通过推迟到包装的任务类型来实现 GetAwaiter() 方法。事实上,下面的测试函数编译得很好,证明TaskWrapper确实是可以等待的:

        static async Task Test()
        {
            var task = Task.FromResult(4);
            _ = await new TaskWrapper<int>(task);
        }

【问题讨论】:

  • 错误信息对我来说似乎非常清楚。解释见副本。这与您的 TaskWrapper&lt;T&gt; 类型有什么关系尚不清楚;错误消息特别与使用async 关键字的方法声明有关。这并不意味着该语言中没有其他“可等待”结构。错误信息没有不一致的地方。
  • 我喜欢有人关闭这个问题的事实,因为据称它在另一个问题中得到了回答——它本身被关闭是因为据说太模糊而无法回答。 (感觉就像一部等待编写的xkcd卡通片)更相关的是,另一个问题不涉及“类似任务的类型”。这个很明显。那么究竟什么是类任务类型呢?我也想知道答案。但是你显然不会在这里得到答案。也许试试github.com/dotnet/csharplang
  • 如果您只是想创建一个可等待的内容,请参阅 Stephen Toubs 博客 devblogs.microsoft.com/pfxteam/await-anything
  • TLDR;等待!=任务之类的。您可以等待您的 TaskWrapper,因为 GetAwaiter() 符合 docs.microsoft.com/en-us/dotnet/csharp/language-reference/…。像类型这样的任务必须有一个构建器,以便编译器/运行时可以构造您的任务并返回它。
  • Tasklike 极为罕见,尤其是现在我们有ValueTask&lt;T&gt;。你到底想用TaskWrapper&lt;T&gt;做什么

标签: c# async-await


【解决方案1】:

从 C# 7.2 开始,编译器支持类似任务的类型。 task type 是具有关联builder 类型 的类或结构,用AsyncMethodBuilderAttribute 标识。示例:

[AsyncMethodBuilder(typeof(TaskWrapperMethodBuilder<>))]
public class TaskWrapper<T>
{
    //...
}

builder 类型 是对应于特定任务类型 的类或结构。 builder 类型应该有以下公共方法:

class TaskWrapperMethodBuilder<T>
{
    public static TaskWrapperMethodBuilder<T> Create();

    public void Start<TStateMachine>(ref TStateMachine stateMachine)
        where TStateMachine : IAsyncStateMachine;

    public void SetStateMachine(IAsyncStateMachine stateMachine);
    public void SetException(Exception exception);
    public void SetResult(T result);

    public void AwaitOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : INotifyCompletion
        where TStateMachine : IAsyncStateMachine;

    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(
        ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : ICriticalNotifyCompletion
        where TStateMachine : IAsyncStateMachine;

    public TaskWrapper<T> Task { get; }
}

有了这个基础设施,您就可以编写返回 TaskWrapper&lt;T&gt; 类型的异步方法。

参考:
Extending the async methods in C# - Task-like types
Async Task Types in C#

这是一个类似任务的类型的实际实现,它是 something useful:
PooledTaskT.cs
PooledTaskMethodBuilderT.cs

【讨论】:

  • 既然我只是想遵循Task类型而不是实现一些自定义状态机,有没有办法指向Task的方法构建器?
  • @Chiel 在没有绝对把握的情况下,我认为你不能不实现专用的 MethodBuilder 类型,即使你的自定义任务类类型直接继承自 Task&lt;T&gt;类。
【解决方案2】:

异步方法必须返回Task。如果您想返回其他内容,请找到一种避免将方法标记为异步的方法。例如,这应该有效:

public static class TestClass
{ 
    static TaskWrapper<int> Test()
    {
        var task = Task.FromResult(4);
        return new TaskWrapper<int>(task);
    }
}

【讨论】:

  • "异步方法必须返回一个任务" - 不,它可以返回一个 task-like 类型,例如ValueTask。这是 C# 7 中的一个新功能。这是一个相对复杂的过程 - 例如,请参阅 devblogs.microsoft.com/premier-developer/…。但这绝对是可能的。
猜你喜欢
  • 1970-01-01
  • 2019-06-23
  • 2020-12-05
  • 1970-01-01
  • 2019-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-16
相关资源
最近更新 更多