async void или как превратить Action в Func<Task>

205
31 марта 2017, 21:42

Есть некий класс, который принимает в конструктор Action<object>. Но мне потребовалось передать туда асинхронный метод (то есть Func<object, Task>). Создал наследника и в конструкторе попытался сделать следующее

public MyClassAsync(Func<object, Task> execute) :
    base (async (o)=>await Task.Factory.StartNew(()=>execute?.Invoke(o)))
{ }

Естественно подчеркивает async и пишет что

asynchronous method anonymous should not return void

А чего мне делать то?

Это не ошибка компилятора. Я насколько понимаю это просто предупреждение о том, что компилятор не знает наверняка как себя вести в такой ситуации. То есть мой код скомпилируется, запустится, но я не знаю наверняка, как он отработает.

UPD:

Я покажу кое что для примера

Вот https://msdn.microsoft.com/ru-ru/library/microsoft.teamfoundation.mvvm.relaycommand(v=vs.110).aspx здесь описание RelayCommand

Вот один из конструкторов

RelayCommand(Action<Object>, Predicate<Object>)

А вот https://msdn.microsoft.com/ru-ru/library/microsoft.teamfoundation.mvvm.asyncrelaycommand(v=vs.110).aspx AsyncRelayCommand : RelayCommand

и вот соответствующий конструктор

AsyncRelayCommand(Func<Object, Task>, Predicate<Object>, Boolean)

К сожалению я не знаю, где посмотреть на исходники этого. Но что-то мне подсказывает, что если в базовый конструктор передать o => Task.Factory.StartNew(...), как предложили в комментариях, то я получу неуправляемую задачу, об окончании которой не смогу узнать.

UPD2

Итак, в конечном счете у меня должны получиться 2 класса

RelayCommand и AsyncRelayCommand : RelayCommand

Подразумевается использовать их так

public RelayCommand SomeCommand {get;set;}
public RelayCommand SomeAsyncCommand {get;set;}
public Ctor()
{
    SomeCommand = new RelayCommand(AnyMethod, Predicate);
    SomeAsyncCommand = new AsyncRelayCommand(AnyMethodAsync, Predicate);
}
async Task AnyMethodAsync()
{
    await SomeTask;
    UIMethod(); // я должен быть уверен, что этот метод выполнится в UI потоке. Конечно, предполагается, что задача сработала посредством взаимодействия пользователя с интерфейсом (по нажатию кнопки или хоткея или еще как)
}

Не получается определить конструктор для AsyncRelayCommand, так как предполагается использование базового конструктора для RelayCommand, а он принимает Action (ну или Action<object>, но это уже вопрос вариативности), а async метод должен быть типа Func<Task> (или Func<object,Task>)

Answer 1

Нет никакой необходимости создавать отдельный класс для запуска асинхронного метода в UI-потоке. Можно использовать для этого обычный RelayCommand если сменить возвращаемое значение на void:

public RelayCommand SomeAsyncCommand { get; } = new RelayCommand(AnyMethodAsync, Predicate);
async void AnyMethodAsync()
{
    await SomeTask;
    UIMethod();
}

Если возврат Task из метода принципиален - то при вызове метода из команды его можно попросту проигнорировать:

public AsyncRelayCommand(Func<object, Task> execute) 
    :base (o => execute(o))
{ }
READ ALSO
Visual Studio 2017 и MVC3

Visual Studio 2017 и MVC3

Подскажите, можно ли запустить в VS 2017 проект MVC3? Вроде, все MVC 3 установлен, но необходимые либы не видит

302
Взаимодействие с календарем Windows 10

Взаимодействие с календарем Windows 10

Когда щелкаешь на часы в Windows 10, открывается панелька часов, календарь и события

209
Не отображается enum в представлении

Не отображается enum в представлении

Всем приветУ меня такой вопрос

208
Как изменить правило разбивки текста на строки в WPF?

Как изменить правило разбивки текста на строки в WPF?

Я использую FormattedText класс чтобы рисовать текст наподобие:

375