Подтвердить что ты не робот

В С# 7, как я могу "свернуть свой собственный" тип типа Task, который будет использоваться с async?

Одной из менее обсуждаемых функций С# 7 является "обобщенные типы возврата async", который описан Microsoft как:

Возвращение объекта Task из методов async может привести к узким местам производительности в определенных путях. Задача является ссылочным типом, поэтому ее использование означает выделение объекта. В тех случаях, когда метод, объявленный с помощью модификатора async, возвращает результат кэширования или завершает синхронно, дополнительные распределения могут стать значительными временными затратами в критических разделах кода производительности. Это может стать очень дорогостоящим, если эти распределения происходят в плотных циклах.

Новая функция языка означает, что методы async могут возвращать другие типы в дополнение к Task, Task<T> и void. Возвращаемый тип должен удовлетворять шаблону async, то есть метод GetAwaiter должен быть доступен. В качестве одного из конкретных примеров тип ValueTask был добавлен в среду .NET для использования этой новой языковой функции:

Это звучит здорово, но я не могу на всю жизнь найти какой-нибудь пример, который не просто использует тип запаса ValueTask<T>. Я хочу создать свой собственный тип типа Task. В частности, я хочу тип, который ведет себя как Task<T>, но с более функциональным стилем обработки ошибок.

Вот тип, который я использую для обработки функциональных ошибок в моем проекте:

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;
    }
}

Вот что я думаю, что мой пользовательский ожидаемый тип должен выглядеть так:

public class TryTask<T> : Task<Try<T>> {

    public TryTask(Func<Try<T>> func)
        : base(func) { }

    //GetAwaiter is defined on base type, so we should be okay there
}

Все это компилируется, пока я не попытаюсь использовать его как возвращаемый тип async:

async TryTask<int> DoWhatever() {
    return await new TryTask<int>(() => new Try<int>(1));
}

Этот метод даст ошибку компилятора. Тип возврата метода async должен быть недействительным, Задача или Задача.

Как мне сделать это или что-то вроде этого компилировать?


Update:

Чтобы подтвердить, я использую выпуск VS 2017 от 3/7, и я могу использовать другие функции С# 7 в моем проекте, такие как локальные функции.

Я также пробовал использовать ValueTask и получаю ту же ошибку компилятора.

static async ValueTask<int> DoWhatever() {
    return await new ValueTask<int>(1);          
}

Вот еще одно сообщение, которое проливает свет на то, что происходит.
Как получить новую семантику async, работающую в VS2017 RC?

По-видимому, должен быть определен отдельный тип "построитель метода", и специальные атрибуты должны применяться к ожидаемому типу. Я не знаю, есть ли у меня время, чтобы вникать в это. Это скорее похоже на метапрограммирование хакера, чем "языковая функция".

4b9b3361

Ответ 1

Я еще не нашел хорошего учебника. Но вы можете посмотреть unittests компилятора, которые создают такие типа типа задач (ищите "[AsyncMethodBuilder" ).

Отправной точкой является создание типа и пометка его как задачи с атрибутом типа [AsyncMethodBuilder(typeof(MyTaskBuilder))]. Затем вам нужно определить свой собственный тип MyTaskBuilder. Он должен реализовать определенный шаблон (см. Ниже). Это тот же шаблон, который реализуется с помощью обычного AsyncMethodBuilder, который поддерживает обычный Task.

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 { }
}

Обновление: малая спецификация для типа задач была добавлена ​​в документы компилятора.