我有一个类用作我所有ViewModel
的父级。它包含一个用于调用其他方法的特定方法,并显示错误加载消息和消息框(主要是):
public class BaseViewModel
{
async public void Try(Func<Task> action, string errorMessage = null, string waitMessage = null)
{
try
{
if (waitMessage != null)
ShowLoading(waitMessage);
await action();
}
catch (Exception e)
{
ShowError(errorMessage, e);
}
finally
{
HideLoading();
}
}
}
它是异步的,所以我的ShowLoading
可以设置动画,就像那样。
它总是会得到匿名(lambda)无参数Task
。我的主要问题是如何实际构建这些Task
。我们假设我在Command
的孩子中有一个ViewModelBase
,在执行时会调用以下方法:
private void OnMyCommandExecute()
{
Try(() =>
{
Thread.Sleep(5000);
}, "error", "please wait");
}
因为Not all code paths return a value in lambda expression of type 'System.Func<System.Threading.Tasks.Task>'
而无法编译。很明显,因为我们await
这个Func
。这引出了我的第二个问题:
Try
调用内容放在哪里才能使其正常工作? 我尝试了一些非常丑陋的东西,我真的希望答案有所不同,否则它将是一种可读性的痛苦:
Try(async () =>
{
return await Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
});
}
它没有编译,但在这一点上,它更好。 return
上的错误:Since 'System.Func<System.Threading.Tasks.Task>' is an
异步method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?
答案 0 :(得分:3)
Try
接受一个返回Task
的方法。在您的第一个示例中,您提供的方法是void
。
在第二个示例中,您提供的方法返回Task<Task>
,但尝试在期望Task
(非泛型)的上下文中使用它。
如果你想使用非异步lambda,那么让lambda返回你想要使用的Task
:
Try(()=>Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
}));
如果你想使用async
lambda,那么你需要等待任务而不返回它:
Try(async () => await Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
}));
请注意,在这里使用异步lambda没有任何实际意义。这两个片段的执行方式相同,但第二个片段在代码膨胀中增加了一些额外的开销,以及在运行时实际上并不需要的整个状态机。
答案 1 :(得分:1)
在此示例中,我应该在我的Try调用中放置什么才能使其正常工作?
你需要通过添加(令人惊讶的)async
来创建lambda表达式async
:
Try(async () =>
{
Thread.Sleep(5000);
}, "error", "please wait");
但是,虽然这将使您能够创建一个async
委托,但实际上没有任何异步(它使用Thread.Sleep
阻止调用线程)。如果这只是一个例子,那么:
Try(async () =>
{
await Task.Delay(5000);
}, "error", "please wait");
是一个更好的。如果它根本不使用async
。
是否正确实施?
不是真的。几乎总是应该避免使用async void
(除非在UI事件处理程序中)。请改用async Task
并确保await
返回的任务在某一点上确保操作已完成,没有任何例外。
答案 2 :(得分:0)
为了使Try
尽可能透明,我最终得到了这个。
async public Task Try(Action action, string errorMessage = null, string waitMessage = null)
{
try
{
if (waitMessage != null)
{
ShowLoading(waitMessage);
await Task.Factory.StartNew(() => action());
}
else
action();
}
catch (Exception e)
{
ShowError(errorMessage, e);
}
finally
{
HideLoading();
}
}
因此,当您致电时,您无需使用Task.Factory.StartNew
或async/await
:
Try(() =>
{
Thread.Sleep(5000);
}, "error", "please wait");