我对下面的代码的优雅和最佳实践有疑问。 应用程序的概念:
因为我有两个独立的演示者调用异步方法,其中一个可以在另一个之前完成,所以我在视图中创建了两个属性“DoneTasksWork”,以便能够在演示者中知道每个演示者的任务是否完成完成了。 如果两者都完成,那么UI中的进度设置为100%(并启用其中的所有控件),如果没有进度将保持仍在运行。
这是一个优雅的解决方案吗?考虑到这种情况,只有当完成状态的进度控制在不同类中的异步方法完成时,我可以使用另一个解决方案而不是用作标志的布尔属性吗?
谢谢!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Project.Utilities;
namespace Project
{
public partial class ViewPhase1 : UserControl, IViewPhase1Work1, IViewPhase1Work2
{
private PresenterPhase1Work1 _presenterPhase1Work1;
private PresenterPhase1Work2 _presenterPhase1Work2;
public ViewPhase1()
{
InitializeComponent();
_presenterPhase1Work1 = new PresenterPhase1Work1(this);
_presenterPhase1Work2 = new PresenterPhase1Work2(this);
}
/// <summary>
/// This event is listened by form that invokes this user control and updates progress control.
/// </summary>
public event ProgressChangedEventHandler ProgressChangedEvent;
public void OnProgressHandler(object sender, ProgressChangedEventArgs e)
{
this.Invoke((Action)delegate
{
if (this.ProgressChangedEvent != null)
{
ProgressChangedEvent(this, e);
}
});
}
public void ShowException(string exMessage, MessageBoxButtons bts, MessageBoxIcon icon)
{
MessageBox.Show("", exMessage, bts, icon);
}
public DataGridView GridInvoices { get; set; }
public DataGridView GridReceipts { get; set; }
public DataGridView GridProducts { get; set; }
public DataGridView GridCustomers { get; set; }
bool IViewPhase1Work1.DoneTasksWork { get; set; }
bool IViewPhase1Work2.DoneTasksWork { get; set; }
}
public interface IViewProgress
{
event ProgressChangedEventHandler ProgressChangedEvent;
void OnProgressHandler(object sender, ProgressChangedEventArgs e);
}
public interface IViewPhase1Work1 : IViewProgress, IViewException
{
bool DoneTasksWork { get; set; }
DataGridView GridProducts { get; set; }
DataGridView GridCustomers { get; set; }
}
public interface IViewPhase1Work2 : IViewProgress, IViewException
{
bool DoneTasksWork { get; set; }
DataGridView GridInvoices { get; set; }
DataGridView GridReceipts { get; set; }
}
public interface IViewException
{
void ShowException(string exMessage, MessageBoxButtons bts, MessageBoxIcon icon);
}
public class PresenterPhase1Work1
{
private readonly IViewPhase1Work1 _view;
public PresenterPhase1Work1(IViewPhase1Work1 view)
{
_view = view;
GetInformation();
}
private void GetInformation()
{
try
{
var task1 = Task.Run(() => GetDataProducts());
var task2 = Task.Run(() => GetDataCustomers());
Task.WhenAll(task1, task2);
_view.GridProducts.DataSource = task1.Result;
_view.GridCustomers.DataSource = task2.Result;
}
catch (Exception ex)
{
_view.ShowException(ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
_view.DoneTasksWork = true;
_view.OnProgressHandler(this, new ProgressChangedEventArgs(_view.DoneTasksWork && _view.DoneTasksWork ? 100 : 50, _view.DoneTasksWork && _view.DoneTasksWork ? "Done" : "Getting data"));
}
}
private async Task<object> GetDataCustomers()
{
return await Util.GetDataCustomerAsync();
}
private async Task<object> GetDataProducts()
{
return await Util.GetDataProductsAsync();
}
}
public class PresenterPhase1Work2
{
private readonly IViewPhase1Work2 _view;
public PresenterPhase1Work2(IViewPhase1Work2 view)
{
_view = view;
GetInformation();
}
private void GetInformation()
{
try
{
var task1 = Task.Run(() => GetDataInvoices());
var task2 = Task.Run(() => GetDataReceipts());
Task.WhenAll(task1, task2);
_view.GridInvoices.DataSource = task1.Result;
_view.GridReceipts.DataSource = task2.Result;
}
catch (Exception ex)
{
_view.ShowException(ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
_view.DoneTasksWork = true;
_view.OnProgressHandler(this, new ProgressChangedEventArgs(_view.DoneTasksWork && _view.DoneTasksWork ? 100 : 50, _view.DoneTasksWork && _view.DoneTasksWork ? "Done" : "Getting data"));
}
}
private async Task<object> GetDataInvoices()
{
return await Util.GetDataInvoicesAsync();
}
private async Task<object> GetDataReceipts()
{
return await Util.GetDataReceiptsAsync();
}
}
}
答案 0 :(得分:1)
优雅的解决方案不是。让我解释一下。
您希望在不使用Task.WhenAll(task1, task2);
的情况下调用await
会发生什么?
我会告诉你:它直接运行并将阻塞_view.GridInvoices.DataSource = task1.Result;
,然后也可能在下一行。
所以,为了解决这个方法(在PresenterPhase1Work1
中,但它也适用于其他演示者):
private void GetInformation()
{
try
{
var task1 = Task.Run(() => GetDataProducts());
var task2 = Task.Run(() => GetDataCustomers());
Task.WhenAll(task1, task2);
_view.GridProducts.DataSource = task1.Result;
_view.GridCustomers.DataSource = task2.Result;
}
catch (Exception ex)
{
_view.ShowException(ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
_view.DoneTasksWork = true;
_view.OnProgressHandler(this, new ProgressChangedEventArgs(_view.DoneTasksWork && _view.DoneTasksWork ? 100 : 50, _view.DoneTasksWork && _view.DoneTasksWork ? "Done" : "Getting data"));
}
}
可以更改为:
private async Task GetInformationAsync()
{
try
{
var task1 = Task.Run(() => GetDataProducts());
var task2 = Task.Run(() => GetDataCustomers());
await Task.WhenAny(task1, task2);
_view.OnProgressHandler(this, new ProgressChangedEventArgs(50, "Getting data"));
await Task.WhenAll(task1, task2);
_view.OnProgressHandler(this, new ProgressChangedEventArgs(100, "Done"));
_view.GridProducts.DataSource = await task1;
_view.GridCustomers.DataSource = await task2;
}
catch (Exception ex)
{
_view.ShowException(ex.Message, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
现在,在构造函数中调用异步方法是个坏主意。构造函数不允许使用async
关键字。所以你必须重新设计你的课程。所以我会将GetInformation
方法设为public并调用并等待视图中其他位置的方法。在您的视图中调用await presenterPhase1Work1.GetMethodAsync();
后,您立即知道该项工作已完成。所以不再需要这个布尔标志。
这确实意味着您的ViewPhase1
需要另一种方法来调用
await presenterPhase1Work1.GetMethodAsync();
方法,因为你也不能在构造函数中执行此操作。
在构造函数中等待完成数据加载从来都不是一个好主意恕我直言,无论是否在后台线程中完成。