Какова сигнатура метода для передачи асинхронного делегата?

Недавно я вернулся к C# из страны Objective-C, и ключевые слова async/await в C# 5 выглядят круто. Но я все еще пытаюсь разобраться с правильным синтаксисом.

Я хочу объявить метод, который принимает асинхронный делегат в качестве параметра, но у меня возникают проблемы с правильным синтаксисом как вызывающего, так и вызываемого. Может ли кто-нибудь предоставить пример кода, показывающий объявление метода, вызов и вызов делегата?

Я думаю, что объявление будет примерно следующим. Обратите внимание, что эта функция не является асинхронной; то есть его асинхронность не зависит от делегата.

void DoSomethingWithCallback(async delegate foo(int)) 
{
    ...
    foo(42);
    ...
}

Вызов будет примерно таким:

DoSomethingWithCallback(async (int x) => { this.SomeProperty = await SomeAsync(x); });

Конечно, ничего из этого не компилируется, и большинство примеров, которые я видел, предполагают, что у них есть поле или свойство, которое является делегатом, а не анонимным делегатом, который я хотел бы использовать.


person AndrewS    schedule 14.12.2011    source источник
comment
насколько я могу судить, нет способа указать встроенный делегат, как я сделал в вопросе выше. Чтобы использовать делегат в качестве параметра метода, необходимо объявить тип делегата вне области действия класса.   -  person AndrewS    schedule 15.12.2011


Ответы (3)


Функция, которая принимает делегат в качестве параметра, должна использовать именованный тип делегата; в отличие от Objective-C, вы не можете объявить анонимный тип делегата встроенным в определение функции. Однако дженерики Action‹> и Func‹> предоставляются, так что вам не нужно объявлять новый тип самостоятельно. В приведенном ниже коде я предполагаю, что делегат принимает один int в качестве параметра.

void DoSomethingWithCallback(Func<int,Task> callbackDelegate)
{
    Task t = callbackDelegate(42);
}

Если эта функция на самом деле ничего не делает с возвращаемым объектом Task (как в коде, показанном выше), вы можете вместо этого использовать Action<int> в качестве типа делегата. Если вы используете действие, вы все равно можете объявить делегат асинхронным (ниже), но возвращаемый неявный объект Task игнорируется.

Синтаксис лямбда для вызова вышеуказанной функции прост, и синтаксис, который вы использовали в вопросе, верен. Обратите внимание, что здесь не нужно указывать тип параметра, так как его можно вывести:

DoSomethingWithCallback(async (intParam) => { this.myint = await Int2IntAsync(intParam); });

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

async Task MyInt2Int(int p) { ... }
Func<int,Task> myDelegate;
void OtherMethod()
{
    myDelegate = MyInt2Int;
    DoSomethingWithCallback(myDelegate); // this ...
    DoSomethingWithCallback(MyInt2Int);  // ... or this.
}
person AndrewS    schedule 15.12.2011

Тип возвращаемого значения сигнатуры метода — Task, если тип возвращаемого значения отсутствует, или Task<T>, если тип возвращаемого значения есть.

Тем не менее, я не уверен на 100 %, что у вас могут быть такие асинхронные лямбда-выражения.

В методе, который использует задачу, вы должны либо «ожидать» задачу, либо использовать свойства и методы в задаче, чтобы получить результат.

person John Gietzen    schedule 14.12.2011
comment
Да, мы поддерживаем асинхронные лямбды. Довольно ловко, если я сам так говорю. - person Eric Lippert; 15.12.2011

Если у меня есть задача, которую я хочу передать, но не выполнить, я могу обернуть задачу в Func<>, а затем вызвать эту Func<> для создания этой задачи. await можно использовать как обычно.

public class Example {
    public Example(Func<Task> toBeExecutedInTheFuture)
    {
        FutureTask = toBeExecutedInTheFuture;
    }

    public async void ExecuteTaskExample()
    {
        await FutureTask();

        // or alternatively

        var myTask = FutureTask();
        // do work
        await myTask;
    }
}
person Dan    schedule 16.06.2020