文章目录
前言
为啥写这个?
因为本人间断性的会进行winform的开发, 对各种跨线程的处理,以及UI的处理.每次都忘记怎么写.都要重新百度,效率很低.
这里记录总结一下.也分项给有需要的朋友 哈哈哈
几个概念
什么是委托delegate
刚开始接触的时候,觉得太TM绕了(我智商低).自己总结一下. 委托就是一个方法行为的集合 , 执行委托 就相当于执行一些列的行为
一个委托可以只有一个方法,可以一个方法都没有,也可以有N个方法
说人话就是 ,委托就是一个代理委托人,我把要执行的任务事情(function)告诉他,他收集好之后,等待我的命令,我让他执行,他就把事情都给我办了.
为什么要委托
-
可以解耦. 只要参数类型一致的方法,都可以塞进一个委托里面.那么在开发者A的角度.我只要执行这个委托就可以了. 具体这个委托要做的动作细节,开发者B自己去实现
-
跨线程UI操作.这个是我用得最多的. 界面卡死,无响应,报错等等,大部分和UI进程阻塞有关. 合理的方法应该是:
- 主线程就负责UI界面的调度.
- 涉及到耗时的动作,全部扔给线程去异步处理.
- 处理完成后,通过invoke委托的方法,来触发UI刷新(因为子线程invoke只能是委托,所以要用委托. 是吗? 我也不确定,哈哈)
-
其他应该还有点别的用处,但是我用不上
直接上代码例子
直接来个最简单的写法
这个目前我认为最简单而已,仅仅是我认为.
即通过lamda表达式 和thread 来实现委托异步回调
//定义委托 作用:回调主界面UI
delegate void DelegateHandleUI(String str);
private void btn_Getversion_threadlamda_Click(object sender, EventArgs e)
{
//声明一个线程异步执行
Thread t1 = new Thread(delegate()
{
//线程里面执行 耗时的动作
JObject jo = getVersion();
//耗时动作结束后 通过lamda表达式 直接写逻辑invoke 委托,执行页面UI刷新方法 ;
this.Invoke(new DelegateHandleUI((a) =>
{
textBox1.Text = a;
}), new Object[] { jo.ToString() });
});
//执行这个线程
t1.Start();
}
这里没有额外声明函数,直接通过lamda去定义了一个委托动作来实现.
稍微复杂完整的写法
//定义委托 作用:回调主界面UI
delegate void DelegateHandleUI(String str);
private void btn_Getversion_thread_Click(object sender, EventArgs e)
{
//告诉委托 要委托执行哪个方法
DelegateHandleUI delegateHandleUI = UIshow;
//声明一个线程异步执行
Thread t1 = new Thread(delegate()
{
//线程里面执行 耗时的动作
JObject jo = getVersion();
//耗时动作结束后 invoke 委托,执行页面UI刷新方法 ;
this.Invoke(delegateHandleUI, new Object[] { jo.ToString() });
//delegateHandleUI("888"); 直接这样写 会报 跨线程调用ui控件错误 需要通过上面的 invoke的方式取回调委托
});
t1.Start();
}
public void UIshow(String str)
{
textBox1.Text = str;
}
这里相比上面, 多了一个 预先定义方法,然后将方法绑定到委托的动作
好处是可以类似这样
DelegateHandleUI delegateHandleUI += UIshow1;
DelegateHandleUI delegateHandleUI += UIshow2;
同时塞入多个方法给一个委托. 让委托一次执行多个动作
来个Action的写法
public partial class Form1 : Form
{
delegate void DelegateHandleUI(String str);
public Form1()
{
InitializeComponent();
}
public void UIshow(String str)
{
textBox1.Text = str;
}
/// <summary>
/// 这里只是回调 ,一般不同个这里的 IAsyncResult ar 来处理控件
/// 这里仍然是线程的方法
/// </summary>
/// <param name="ar"></param>
private void MyAsynCallback(IAsyncResult ar)
{
// 这时候的ar.AsyncState 就是 "123"
//textBox1.Text = ar.AsyncState.ToString(); 注意这里仍然是 线程的动作.调用控件会犯法
Console.WriteLine("异步调用结束");
// Console.ReadLine();
}
private void btn_Getversion_Click(object sender, EventArgs e)
{
Action action = (() =>
{
#region 需要等待的硬件调用
object obj = SiReader.getVerion();
JObject jo = JObject.FromObject(obj);
#endregion
#region 通过委托方法 刷新主线程UI
DelegateHandleUI delegateHandleUI = new DelegateHandleUI(UIshow);
this.Invoke(delegateHandleUI, new object[] { jo.ToString() });
#endregion
});
//必须定义一个AsyncCallback 哪怕不处理任何动作
AsyncCallback asyncCallBack = new AsyncCallback(MyAsynCallback);
#region 异步执行action
action.BeginInvoke(asyncCallBack, "123"); //这里传123 就是看看MyAsynCallback的 ar.AsyncState 能不能获取到123
#endregion
#region 以下3个写法 等效, 属于同步 不是异步!!
//this.BeginInvoke(action); //这样是同步 不是异步!!
//this.Invoke(action);
//action();
#endregion
}
}
这个稍微麻烦的就是需要实现一个AsyncCallback 方法才能实现异步
有强迫的朋友就别用了
来个Action带入参的写法
private void btn_Getversion_Click(object sender, EventArgs e)
{
//Action action = (() =>
Action<int ,int> action = ((a,b) =>
{
#region 需要等待的硬件调用
JObject jo = getVersion();
#endregion
Console.WriteLine("测试Action 传参数:" + (a + b).ToString());
#region 通过委托方法 刷新主线程UI
DelegateHandleUI delegateHandleUI = new DelegateHandleUI(UIshow);
this.Invoke(delegateHandleUI, new object[] { jo.ToString() });
#endregion
});
//必须定义一个AsyncCallback 哪怕不处理任何动作
AsyncCallback asyncCallBack = new AsyncCallback(MyAsynCallback);
#region 异步执行action
//后面两个参数固定, 第一个 和 第二个 参数为 定义Aciton时候的入参
action.BeginInvoke(3,4,asyncCallBack, "123"); //这里传123 就是看看MyAsynCallback的 ar.AsyncState
#endregion
#region 以下3个写法 等效, 属于同步 不是异步!!
//this.BeginInvoke(action); //这样是同步 不是异步!!
//this.Invoke(action);
//action();
#endregion
}