c# 委托探究
什么时候使用委托呢?
1.程序中有的方法很耗时,而调用这个耗时方法返回的结果值,需要在接下来的代码中使用到,这时使用委托,将耗时方法与委托绑定(Func),在使用委托的异步调用BeginInvoke,程序代码接着处理其它项,需要时使用 Func.EndInvoke(AsyncResult)得到耗时方法的结果值
(除了这种需要使用返回值的情况,假设耗时方法去完成界面的一部分工作,而另一部分工作由其它代码块完成,使用委托的异步调用,二者并行执行,缩短程序执行时间)
2.async/await 与Task.Run() 结合使用,异步调用,并且界面上不会卡死(经测试,第1种调用耗时方法时界面无法响应用户其它的操作),Task是线程池的升级版,使用Task.Run不一定会创建新线程,也可能在空闲的线程执行,查看Task.Run,如下图,也是结合委托来使用的
3.父子界面,父子控件 可以使用委托和事件, 还有常见的观察者模式(一对多),比如猫叫了,老鼠跑了 主人醒了
上面两种具体的使用参考:
一.委托类别
1)delegate
delegate至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型
例:public delegate int MethodtDelegate(int x, int y);表示有两个参数,并返回int型
上述委托绑定的方法也必须是返回类型为int,参数为int,且为2个
2)Action Action是无返回值的泛型委托。
Action 表示无参,无返回值的委托
Action<int> 表示有传入参数int;无返回值的委托
Action<int,string> 表示有传入参数int,string ;无返回值的委托
......
3)Func
Func是有返回值的泛型委托
Func<int> 表示无参,返回值为int的委托
Func<int,string>表示传入参数为int类型,返回值为string类型
.......
Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void
二、委托调用
1.同步调用
Invoke
2.异步调用
BeginInvoke,
BeginInvoke 方法参数不定,但是最后两个参数固定,其中倒数第2个参数,到时第1个参数为必须,若没有,传入null,
倒数第2个参数表示回调函数,倒数第1个参数表示传给回调函数的参数内容
三、调用例子代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication1 { public partial class frmWeiTuo : Form { public frmWeiTuo() { InitializeComponent(); } public delegate string testEventHander(string arg); private void btnAction_Click(object sender, EventArgs e) { this.richTextBox1.Text = ""; //Action委托,Func 委托 作为方法的类型, //Test(msg, "huanggang"); //Test(msg, 30); TestFunc(msgFunc,"shifang"); TestFunc(msgFunc,31); //delegate 委托 //将方法msgDelegate 与委托绑定, //testEventHander testEvent = new testEventHander(msgDelegate); //MessageBox.Show( msgDelegate("huanggang")); } /// <summary> /// Action是无返回值的泛型委托 例如Action<int,string> 表示有传入参数int,string无返回值的委托 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="action"></param> /// <param name="p"></param> public void Test<T>(Action<T> action, T p) { //action(p); // action.Invoke(p); //同步调用 action.BeginInvoke(p,null,null); //异步调用 this.richTextBox1.Text += "Test" + "\r\n"; } /// <summary> /// Func是有返回值的泛型委托 /// T1为传入参数,T2为函数返回类型 比如Func(string,int) 表示传入参数是string类型,函数返回类型为int /// BeginInvoke 方法参数不定,但是最后两个参数固定,其中倒数第2个参数,到时第1个参数为必须,若没有,传入null, /// 倒数第2个参数表示回调函数,倒数第1个参数表示传给回调函数的参数内容 /// </summary> /// <typeparam name="T1"></typeparam> /// <typeparam name="T2"></typeparam> /// <param name="func"></param> /// <param name="arg"></param> public void TestFunc<T1,T2>(Func<T1,T2> func,T1 arg) { // this.richTextBox1.Text += func(arg); // this.richTextBox1.Text += func.Invoke(arg);//同步 IAsyncResult asyncResult = func.BeginInvoke(arg, null, null);//异步调用, var result= func.EndInvoke(asyncResult); //接收委托返回的值 this.richTextBox1.Text += result; } public void msg(string name) { // 当使用 action.BeginInvoke(p,null,null) 调用时,会报错,提示从不是当前的线程访问 // this.richTextBox1.Text += name+"\r\n"; //action.BeginInvoke(p,null,null) 调用 ,使用线程匿名委托 delegate (){} 匿名委托写法 richTextBox1.BeginInvoke(new ThreadStart(delegate () { richTextBox1.Text += name + "\r\n"; })); } public void msg(int age) { richTextBox1.BeginInvoke(new ThreadStart(delegate() { richTextBox1.Text += age + "\r\n"; })); } public string msgFunc(string name) { //this.richTextBox1.Text += name + "\r\n"; return name + "\r\n"; } public int msgFunc(int age) { return age; } public string msgDelegate(string name) { //this.richTextBox1.Text += name + "\r\n"; return name + "\r\n"; } public int msgDelegate(int age) { return age; } private void btnAsync_Click(object sender, EventArgs e) { Action<string> ac = msg; ac("huanggang"); //匿名函数的写法,Lambda写法 ,可以理解为委托类型的对象 Action<string> ac1 = (t) => { msg(t); }; Action<int> ac2 = (a) => { msg(a); }; ac1.Invoke("石芳"); ac2.Invoke(30); testEventHander testEvent = msgDelegate; testEvent("30"); } } }
以上,若不有不对的,大家多批评指正,谢谢!
本文参考链接: