定义:委托是一个类型安全的对象,它指向程序中另一个以后会被调用的方法(或多个方法)。通俗的说,委托是一个可以引用方法的对象,当创建一个委托,也就创建一个引用方法的对象,进而就可以调用那个方法,即委托可以调用它所指的方法。
委托的试用步骤:
1、定义委托:权限修饰符 delegate 返回值 委托名 (参数);
2、声明委托: 委托名 委托对象;
3、将对象与委方法绑定(可以绑定一个方法或者多个方法): 委托对象 = new 委托名(需要绑定的方法名称);
4、使用委托调用方法:委托实例名(参数);
Ps:1、委托和委托绑定的方法需要相同的参数(参数类型和个数都要相同)和返回值;
2、委托可以调用多个方法,委托对象可以维护可调用方法的列表;即多路广播,简称:多播;
3、使用+= 添加绑定方法,-=释放绑定方法;delegate委托可以不带参数,支持最大23个参数;可以没有返回值,也可以带返回值;
委托其中常见的写法有:
1、实例化委托并调用:委托名 委托实例 = new 委托名(委托方法);委托实例(参数);
2、实例化委托并调用 简写:委托名 委托实例 = 委托的方法 ;委托实例(参数);
3、实例化委托并调用 匿名委托:委托名 委托实例 = delegate(参数){待调用的方法体};委托名(参数);
4、实例化委托并调用 lambda表达式写法:委托名 委托实例 = ((参数1,参数2,参数3,参数4,...参数n)=>{待调用的方法体});委托实例(参数1,参数2,参数3,...参数n);
5、在.NET 2.0开始 ,出现Func<in T,out TResult> 和Action<in T>;
Func至少0个参数,至多16个参数,必须有返回值;
Action至少0个参数,至多16个参数,无返回值。
Func<参数1,参数2,...参数n,返回值> 委托实例 = ((参数1,参数2,。。。参数n)=>{带返回值的方法体});
返回值 = 委托实例(参数1,参数2,。。。参数n);
Action<参数> 委托实例 = ((参数)=>{待执行的方法体});委托实例(参数);
Predicate<T>表示定义一组条件并确定指定对象是否符合这些条件的方法,返回值始终为bool类型
申明的原型: public delegate bool Predicate<in T>(T obj) 由于比较陌生;所以下面给一个事例:
1 public bool Match(int val) 2 { 3 return val > 60; 4 } 5 6 Predicate<int> t = new Predicate<int>(Match); //定义一个比较委托 7 int[] arr = { 13, 45, 26, 98, 3, 56, 72, 24 }; 8 int first = Array.Find(arr, t); //找到数组中大于60的第一个元素
下面陆续介绍:
1、Invoke
2、BeginInvoke,EndInvoke
3、Thread
4、线程池
5、线程同步
6、死锁
7、线程同步的几种方法
8、在线程中访问GUI组件
Invoke:
Control.Invoke 方法 (Delegate) :在拥有此控件的基础窗口句柄的线程上执行指定的委托。
Control.BeginInvoke 方法 (Delegate) :在创建控件的基础句柄所在线程上异步执行指定委托。
msdn说明:
控件上的大多数方法只能从创建控件的线程调用。 如果已经创建控件的句柄,则除了 InvokeRequired 属性以外,控件上还有四个可以从任何线程上安全调用的方法,它们是:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。 在后台线程上创建控件的句柄之前调用 CreateGraphics 可能会导致非法的跨线程调用。 对于所有其他方法调用,则应使用调用 (invoke) 方法之一封送对控件的线程的调用。 调用方法始终在控件的线程上调用自己的回调。
主线程调用Invoke:
1 public void TestInvoke() 2 { 3 listBox1.Items.Add("----Add------ "); 4 listBox1.Invoke(new Action(() => { listBox1.Items.Add("Incoke"); })); 5 Thread.Sleep(1000); 6 listBox1.Items.Add("---End-----"); 7 }
主线程调用BeginInvoke:
1 public void TestInvoke() 2 { 3 listBox1.Items.Add("----Add------ "); 4 var b = listBox1.BeginInvoke(new Action(() => { listBox1.Items.Add("BrginInvoke"); })); 5 Thread.Sleep(1000); 6 listBox1.Items.Add("---End-----"); 7 }
只有当调用BeginInvoke的线程结束后,才执行它的内容。
不过有两种情况下,它会马上执行;1、使用EndInvoke,检索由传递的 IAsyncResult 表示的异步操作的返回值。2、同一个控件调用Invoke时,会马上执行先前的BeginInvoke
1、
1 public void TestInvoke() 2 { 3 listBox1.Items.Add("----Add------ "); 4 var b = listBox1.BeginInvoke(new Action(() => 5 { 6 Thread.Sleep(1000); 7 listBox1.Items.Add("BrginInvoke"); 8 })); 9 listBox1.EndInvoke(b); 10 listBox1.Items.Add("---End-----"); 11 }
2.
1 private void TestBeginInvokeInvoke() 2 { 3 listBox1.Items.Add("--begin--"); 4 listBox1.BeginInvoke(new Action(() => 5 { 6 Thread.Sleep(1000); 7 listBox1.Items.Add("BeginInvoke"); 8 })); 9 listBox1.Invoke(new Action(() => 10 { 11 listBox1.Items.Add("Invoke"); 12 })); 13 listBox1.Items.Add("--end--"); 14 }
注:在主线程中直接调用Invoke、BeginInvoke、EndInvoke都会造成阻塞。所以应该利用副线程(支线线程)调用
- Invoke的委托在主线程中执行
- Invoke在支线程中调用也会阻塞主线程
- Invoke还会阻塞支线程