一、委托案例引入
1.案例一:
首先新建一个控制台项目委托案例一,一个类库委托类库一,在类库一中的类Class1中编写Do()方法。然后在控制台项目委托类型一种输出,代码如下:
Class1中的代码:
1 namespace 委托类库一 2 { 3 public class Class1 4 { 5 public void Do() 6 { 7 Console.WriteLine("======================================"); 8 9 Console.WriteLine("======================================"); 10 11 Console.WriteLine("======================================"); 12 13 Console.WriteLine("======================================"); 14 } 15 } 16 }
委托类型一中的代码:
1 namespace 委托案例一 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Class1 c = new Class1(); 8 c.Do(); 9 10 Console.ReadKey(); 11 } 12 } 13 }
输出结果显而易见。
现在我要在输出的结果(四条双等线)中间再输出当前时间,于是我在Class1中又增加一个方法,并且在Do()中调用,如下:
1 namespace 委托类库一 2 { 3 public class Class1 4 { 5 public void Do() 6 { 7 Console.WriteLine("======================================"); 8 9 Console.WriteLine("======================================"); 10 11 GetCurrentDate(); 12 13 Console.WriteLine("======================================"); 14 15 Console.WriteLine("======================================"); 16 } 17 18 public void GetCurrentDate() 19 { 20 Console.WriteLine(DateTime.Now.ToString()); 21 } 22 } 23 }
现在委托案例一中输出的结果如下:
======================================
======================================
2017/4/25 17:37:21
======================================
======================================
现在我想做一些改变,又新建一个项目,委托类型1,在这个控制台中我想把当前时间保存到文本文件中,这样,我就得改动Do()中调用的方法GetCurrentDate()的方法体,如果有N个控制台项目,都要调用委托类库一的Class1中的方法,并且每个控制台项目中,要求在双等线中间要做的操作不一样,那我就需要在Class1中写N个方法,并且切换一次控制台项目,就要改动Do()中调用的方法,这是一件很麻烦的事情。有其他方式解决吗?
如果在Do()方法中用一个变量代替GetCurrentDate()这个方法,我们在控制台项目中需要做什么操作,把需要的操作(方法)传给这个变量,那么问题就可以解决了。把方法传给一个变量,这就需要用到委托了。
如果我把Do()中的方法用一个委托变量代替,容易想到,我要创建的委托也应该放在当前类库中。所以在当前类库中添加一个类,改造成委托,然后在需要使用委托变量的Class1这个类中声明委托变量,在Do()方法中调用。代码如下:
1.定义委托类型:
1 namespace 委托类库一 2 { 3 public delegate void E1Delegate();//委托和抽象方法类似,只是关键字不同 4 }
2.在使用的类中声明委托变量,并且调用:
1 namespace 委托类库一 2 { 3 public class Class1 4 { 5 6 public E1Delegate method;//成员变量,默认值是null 7 public void Do() 8 { 9 Console.WriteLine("======================================"); 10 11 Console.WriteLine("======================================"); 12 13 //GetCurrentDate(); 14 15 16 if(method!=null) 17 { 18 method();//用委托变量调用 19 } 20 21 22 23 24 Console.WriteLine("======================================"); 25 26 Console.WriteLine("======================================"); 27 } 28 29 //public void GetCurrentDate() 30 //{ 31 // Console.WriteLine(DateTime.Now.ToString()); 32 //} 33 } 34 }
3.在控制台程序中创建要绑定的方法,并且把方法赋值给委托变量:
1 namespace 委托案例一 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Class1 c = new Class1(); 8 //c.Do(); 9 c.method = GetCurrentDate;//把方法赋值给委托变量的时候,方法不需要带括号 10 c.Do(); 11 12 Console.ReadKey(); 13 } 14 15 static void GetCurrentDate() 16 { 17 Console.WriteLine(DateTime.Now.ToString()); 18 } 19 } 20 }
此时,在委托案例一这个项目就已经实现了本文开始的效果。在委托案例1控制台程序的代码如下:
1 namespace 委托案例1 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Class1 c = new Class1(); 8 //c.Do(); 9 c.method = GetCurrentDate;//把方法赋值给委托变量的时候,方法不需要带括号 10 c.Do(); 11 12 Console.ReadKey(); 13 } 14 15 static void GetCurrentDate() 16 { 17 File.WriteAllText("datetime.txt",DateTime.Now.ToString()); 18 Console.WriteLine("当前时间已经写入到了txt文件中"); 19 } 20 } 21 }
委托案例一和委托案例1运行结果对比:
案例一:
======================================
======================================
2017/4/25 19:16:45
======================================
======================================
案例1;
======================================
======================================
当前时间已经写入到了txt文件中
======================================
======================================
以上就是委托的使用场景和过程。
当然,上面的委托使用过程还能进一步优化:
在使用委托变量的Class1这个类中,我们可以把成员变量,改成局部变量(Do()方法的参数)。然后在控制台程序中直接把方法作为参数通过Do()传递。代码如下:
Class1中:
1 namespace 委托类库一 2 { 3 public class Class1 4 { 5 6 //public E1Delegate method;//成员变量,默认值是null 7 public void Do(E1Delegate method) 8 { 9 Console.WriteLine("======================================"); 10 11 Console.WriteLine("======================================"); 12 13 //GetCurrentDate(); 14 15 16 if(method!=null) 17 { 18 method();//用委托变量调用 19 } 20 21 22 23 24 Console.WriteLine("======================================"); 25 26 Console.WriteLine("======================================"); 27 } 28 29 //public void GetCurrentDate() 30 //{ 31 // Console.WriteLine(DateTime.Now.ToString()); 32 //} 33 } 34 }
控制台程序:
1 namespace 委托案例一 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 Class1 c = new Class1(); 8 //c.Do(); 9 //c.method = GetCurrentDate;//把方法赋值给委托变量的时候,方法不需要带括号 10 c.Do(GetCurrentDate); 11 12 Console.ReadKey(); 13 } 14 15 static void GetCurrentDate() 16 { 17 Console.WriteLine(DateTime.Now.ToString()); 18 } 19 } 20 }
留个练习:
给个指定的数组,不同的调用者给每个数组中的元素添加的前后缀不同,用委托实现。
二、案例二:简单模拟两个窗口聊天
如图:我在左边的窗口的发送的消息文本框中,输入发送的消息,然后点击发送消息,弹出新窗口,并且同步显示刚才发送的消息。接下来在右边的回复消息文本框中,输入回复消息,点击回复后,左边的窗口中的未读消息中马上显示刚才回复的消息,同时右边的窗口关闭。
这个功能有很多实现方式,这里我们之关注如何用委托实现。
分析:
1.在未读消息中显示右边窗口(FrmPostBackMsg)回复的消息,最终是要把消息内容在未读消息的文本框中显示出来,所以最终会执行给未读的文本框赋值的代码,并且只能在左边的窗口(FrmSendMsg)文件中,才能给未读消息的文本框赋值。所以需要在左边的窗口文件中设计一个方法(UpdateText(string s))实现这个赋值功能。
2.我们需要在右边的窗口关闭时候,把回复消息同步到左边的窗口中,也就是说,我们在点击回复的时候,需要调用左边窗口中的赋值方法,可是这个方法在别处,不能直接调用,有什么办法可以调用呢?这时候,我们想到了委托,用一个委托变量把它装起来,带到右边的窗口中就行。
3.那我们在哪声明委托变量,在哪装方法,在哪取方法呢?因为方法在左边的窗口中,所以肯定需要在左边的窗口中装方法,装好后传到右边的窗口中,所以在弹出右边的窗口的时候,需要传入这个方法,然后在右边的窗口的构造函数中取到方法(构造函数的一个参数是委托,用来接收传过来的方法),然后在点击回复的时候,调用这个委托变量。当然,如果要使用这个委托变量,必须把它提升为全局变量。代码如下:
左边的窗口:
1 public partial class FrmSendMsg : Form 2 { 3 public FrmSendMsg() 4 { 5 InitializeComponent(); 6 } 7 8 private void btnSendMsg_Click(object sender, EventArgs e) 9 { 10 FrmPostBackMsg postBackMsg = new FrmPostBackMsg(txtMsg.Text.Trim(),UpdateText); 11 postBackMsg.Show(); 12 } 13 14 private void UpdateText(string s) 15 { 16 this.txtReceiveMsg.Text = s; 17 } 18 }
右边的窗口:
1 public partial class FrmPostBackMsg : Form 2 { 3 DelegatePostBackMsg _delegateMsg; 4 public FrmPostBackMsg() 5 { 6 InitializeComponent(); 7 } 8 9 public FrmPostBackMsg(string msg, DelegatePostBackMsg delegateMsg) :this() 10 { 11 txtMsg.Text = msg; 12 this._delegateMsg = delegateMsg; 13 } 14 15 private void btnPostBack_Click(object sender, EventArgs e) 16 { 17 if (_delegateMsg!=null) 18 { 19 _delegateMsg(txtPostBackMsg.Text.Trim()); 20 } 21 22 this.Close(); 23 } 24 }
三、委托的实质
委托实质上是一个类,它继承于它继承自MulticastDelegate,Delegate是所有委托的祖宗类。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 #region MyRegion 6 7 //DelegateM1 m1 = M1; 8 ////反编译后等价于 DelegateM1 m1 = new DelegateM1(M1);就是说委托变量只能接受委托对象,我们把方法赋值给委托,其实内部是把方法作为委托的参数传了进去 9 10 //m1();//调用委托的时候,写成方法的形式,实际上是调用委托的 m1.Invoke();方法 11 12 //委托实际上是一个类,它继承自MulticastDelegate,MulticastDelegate又继承自Delegate(所有的委托都继承与它) 13 14 #endregion 15 16 DelegateM2 m2 = new DelegateM2(M2); 17 int res= m2(1,2); 18 Console.WriteLine(res); 19 20 Console.ReadKey(); 21 } 22 23 static void M1() 24 { 25 Console.WriteLine("这是方法M1"); 26 } 27 28 static int M2(int n1,int n2) 29 { 30 return n1 + n2; 31 } 32 } 33 34 public delegate void DelegateM1(); 35 36 public delegate int DelegateM2(int n1,int n2);