在C#7中,我怎么能;“滚我自己的”;要与async一起使用的任务类型?

在C#7中,我怎么能;“滚我自己的”;要与async一起使用的任务类型?,c#,async-await,c#-7.0,C#,Async Await,C# 7.0,C#7的一个较少被提及的特性是“广义异步返回类型”,微软将其描述为: 从异步方法返回任务对象可能会在某些路径中引入性能瓶颈。任务是引用类型,所以使用它意味着分配一个对象。在使用async修饰符声明的方法返回缓存结果或同步完成的情况下,额外的分配可能会在性能关键的代码段中造成很大的时间开销。如果这些分配发生在紧密的循环中,成本可能会非常高 新的语言特性意味着,除了Task、Task和void之外,异步方法还可以返回其他类型。返回的类型必须仍然满足异步模式,这意味着GetAwaiter方法必须是可访

C#7的一个较少被提及的特性是“广义异步返回类型”,微软将其描述为:

从异步方法返回任务对象可能会在某些路径中引入性能瓶颈。任务是引用类型,所以使用它意味着分配一个对象。在使用async修饰符声明的方法返回缓存结果或同步完成的情况下,额外的分配可能会在性能关键的代码段中造成很大的时间开销。如果这些分配发生在紧密的循环中,成本可能会非常高

新的语言特性意味着,除了
Task
Task
void
之外,异步方法还可以返回其他类型。返回的类型必须仍然满足异步模式,这意味着GetAwaiter方法必须是可访问的。作为一个具体示例,ValueTask类型已添加到.NET framework中,以利用此新的语言功能:

这听起来不错,但在我的一生中,我找不到任何不使用stock
ValueTask
类型的示例。我想让我自己的任务类型。具体地说,我想要一种行为类似于任务的类型,但具有更为实用的错误处理风格

以下是我在项目中用于功能错误处理的类型:

public class Try<T> {
    public T Data { get; }
    public Exception Error { get; }

    public bool HasData => Error == null;
    public bool HasError => Error != null;

    public Try(T data) {
        Data = data;
    }

    public Try(Exception error) {
        Error = error;
    }
}

下面是另一篇文章,它揭示了正在发生的事情。


显然,需要定义一个单独的“方法生成器”类型,并且需要将特殊属性应用于等待类型。我不知道我是否真的有时间深入研究这个问题。它看起来更像元编程黑客,而不是“语言特性”。

我还没有找到任何好的教程。 但是您可以查看创建此类任务类型的方法(查找“[AsyncMethodBuilder”)

起点是创建一个类型,并使用类似
[AsyncMethodBuilder(typeof(MyTaskBuilder))]
的属性将其标记为task-like。 然后您需要定义自己的
MyTaskBuilder
类型。它必须实现特定的模式(请参见下文)。这与支持常规
任务的常规类型实现的模式相同

class MyTaskBuilder
{
    public static MyTaskBuilder Create() => null;
    public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { }
    public void SetStateMachine(IAsyncStateMachine stateMachine) { }
    public void SetResult() { }
    public void SetException(Exception exception) { }
    public MyTask Task => default(MyTask);
    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 { }
}
类MyTaskBuilder
{
公共静态MyTaskBuilder Create()=>null;
public void Start(ref TStateMachine stateMachine),其中TStateMachine:IAsyncStateMachine{}
public void SetStateMachine(IAsyncStateMachine stateMachine){}
public void SetResult(){}
public void SetException(异常){}
公共MyTask任务=>默认值(MyTask);
public void waitioncompleted(ref TAwaiter waitier,ref TStateMachine stateMachine),其中TAwaiter:inotifyccompletion,其中TStateMachine:IAsyncStateMachine{}
public void waitunsafeoncompleted(ref TAwaiter waiter,ref TStateMachine stateMachine)其中TAwaiter:ICriticalNotifyCompletion其中TStateMachine:IAsyncStateMachine{}
}

更新:编译器文档中添加了a。

只是为了确认……您的项目中是否有其他C#7功能?我可以生成本地函数。我还运行2017版本并针对.NET 4.6.2。这不是黑客行为,您只是使用一个属性通知编译器该类型将被视为“类似任务”。但是,是的,您需要以适当的方式应用该属性。必须应用该属性,并且它接受类型为
type
的参数,该参数必须是某个方法生成器类型。因此,现在我需要定义该类型。但问题还在于,我正在尝试创建一个通用任务类型,因此我的方法生成器应该是generic,但您不能将遗传类型传递给属性参数。除了visual Studio本身之外,您还使用其他插件/扩展吗?这些任务类型的实现细节是什么?我觉得这些是一些需要明确记录的逻辑期望。@DouglasGaskell查看链接的speclet了解更多细节。另外,我我要寻找解释
AsyncMethodBuilder
如何工作的资源。这个答案非常有用,但是单元测试的
AsyncMethodBuilder
属性中使用的生成器有空的实现。我想他们只测试它是否编译。我希望有一个完整的例子……我真的很想这样做创建我自己的类任务类型,以避免嵌套泛型地狱…我反编译了
AsyncTaskMethodBuilder
,但它使用了来自程序集的许多内部依赖项,我不知道它们是做什么的…好吧,我刚刚发现了这一点,但它似乎比我在反编译类中看到的更简单:
async TryTask<int> DoWhatever() {
    return await new TryTask<int>(() => new Try<int>(1));
}
static async ValueTask<int> DoWhatever() {
    return await new ValueTask<int>(1);          
}
class MyTaskBuilder
{
    public static MyTaskBuilder Create() => null;
    public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine { }
    public void SetStateMachine(IAsyncStateMachine stateMachine) { }
    public void SetResult() { }
    public void SetException(Exception exception) { }
    public MyTask Task => default(MyTask);
    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 { }
}