委托、事件学习笔记
学习c#有一段时间了,在工作或者学习中总是碰到委托和事件,行内过来人说委托和事件其实就是一个坎,没过去的总感觉到委托和事件很难理解,而过去的人说也就是那么一回事。本人现在应该是处于正在理解的边缘,托大了,其实狗屁都没理解。关于委托和事件,在网上也看了大量的资料,大牛们好像是每个牛都有自己的理解,小可也只能模仿学习,此文仅仅记录自己的学习笔记。如有侵犯大牛的地方,还望海涵。
委托:
委托,开始的理解是将方法作为一个函数的参数进行调用。这种理解是正确的,只是站在表皮上。前几天又听一同事的讲解,别有一番风味。
“当一个类或者是进程需要调用一个方法时,需要借助另外的类或者是进程进行调用,而这种机制就称为委托”。
两种理解本质上是一样的,但是站的高度不一样,对问题的理解肯定不能同日而语。还有牛说:委托可以理解成为函数指针,不同的是委托是面向对象的,而且是类型安全的。
委托使用的三个步骤:
1、声明一个委托: public delegate void processDelegate;
2、定义一个委托对象:processDelegate pro= new processDelegate(需要调用的方法);
3、调用方法:pro(i);
下面是一个委托示例:
public partial class MainWindow : Window { public delegate void processDelegate(int value); processDelegate pro = null; public MainWindow() { InitializeComponent(); Thread t = new Thread(new ThreadStart(gogogo)); t.Start(); pro = new processDelegate(this.process); } public void process(int value) { this.progressBar1.Dispatcher.Invoke(new Action(()=>{ progressBar1.Value = value; tbText.Text = "执行"; })); } public void gogogo() { for (int i = 0; i < 100; i++) { if (pro != null) { pro(i); } System.Threading.Thread.Sleep(100); } } } <Grid> <ProgressBar Height="35" HorizontalAlignment="Left" Margin="84,109,0,0" Name="progressBar1" Minimum="1" Maximum="100" VerticalAlignment="Top" Width="302" /> </Grid>
事件:
事件可以分为两个部分:事件发送器和事件接收器。事件发生类:这个类中触发了一个事件,但这个类并不知道哪个对象或者方法将会接收并处理它。现在就需要发送方和接收方之间存在一个媒介。而媒介就是委托。
下面具体拿一个例子来说明如何使用事件:
定义一个发送器的类:KeyInputMonitor
声明委托和事件:public delegate void KeyDownHanlder(object sender, keyEventArgs e);
public event KeyDownHanlder KeyDown;
在KeyInputMonitor类中定义一个方法Run,在方法内部触发事件:KeyDown (this ,myKeyEventArgs );
定义一个接收器的类:EventReceiver
类产生一个委托实例,并把这个委托实例添加到产生事件对象的事件列表中:monitor.KeyDown += new KeyInputMonitor.KeyDownHanlder(monitor_KeyDown);
委托调用的方法,也是真正的处理函数:monitor_KeyDown
完整代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Event { class Program { static void Main(string[] args) { KeyInputMonitor myMonitor = new KeyInputMonitor(); EventReceiver myReceiver = new EventReceiver(myMonitor); myMonitor.Run(); } } //派生一个KeyEventArgs类,来保存按键信息 class keyEventArgs : EventArgs { private char keyChar; public keyEventArgs(char KeyChar) : base() { this.keyChar = KeyChar; } public char KeyChar { get { return keyChar; } } } class KeyInputMonitor { public delegate void KeyDownHanlder(object sender, keyEventArgs e); public event KeyDownHanlder KeyDown; public void Run() { bool finished = false; do { Console.WriteLine("input a char..."); string response = Console.ReadLine(); char responseChar = (response == "") ? '\0':char.ToUpper (response [0]); switch (responseChar ) { case 'X': finished = true ; break ; default : keyEventArgs myKeyEventArgs = new keyEventArgs (responseChar ); KeyDown (this ,myKeyEventArgs );//触发事件 break ; } }while (!finished); } } //类先产生一个委托实例,再把这个委托实例添加到产生事件对象的事件列表中去,这个过程又叫订阅事件 class EventReceiver { public EventReceiver(KeyInputMonitor monitor) { monitor.KeyDown += new KeyInputMonitor.KeyDownHanlder(monitor_KeyDown); } void monitor_KeyDown(object sender, keyEventArgs e) { //真正的事件处理函数 Console.WriteLine("capture key:{0}", e.KeyChar); } } }
暂且写到此处,如果在以后有更深层次的理解,再次更改补充吧。
补充1:
委托和事件的区别:
委托:明面上是将方法作为一个函数的参数进行调用,另外一种理解是:当一个类或者是进程需要调用一个方法是,需要借助另外的类或者进程进行调用,这种机制成为委托。本质上来讲:委托是一种类型。
事件是一种特殊的委托类型。
委托和事件的区别:委托可以直接调用委托来激发委托所指向的函数,也可以由服务代码自己触发。事件的触发只能由服务代码自己触发。
通俗来说:委托可以从类内和类外进行调用注册的方法;事件只能从类内调用注册的方法,比委托要安全。
补充2:
1)委托定义:
委托类似于 C 或 C++ 中的函数指针。使用委托将方法引用封装在委托对象内,然后调用该委托对象就可以执行委托对象内方法引用指向的方法,而不必在编译时知道将调用哪个方法。
2)委托本质:
1.声明委托
public delegate void SayHelloDelegate(string who);
2.使用ILSpy反编译后,看其本质
public class auto ansi sealed SayHelloDelegate: MulticastDelegate
编译器自动生成了一个委托类,继承自MulticastDelegate。
3.下图可以说明:
3)调用委托:
1.给委托变量加上()就相当于调用
static void Main(string[] args) { //创建委托变量(使用new关键字) SayHelloDelegate sayDel = new SayHelloDelegate(SayHelloToAmerican); //委托变量调用 sayDel("jack"); }
2.反编译看其本质是调用了Invoke方法
所以我们自己也可以直接调用这个方法,委托变量调用的这两种方法,本质是一样的,编译器都会调用Invoke。简写方式也是语法糖。
sayDel("jack"); sayDel.Invoke("jack");
3.委托变量的调用本质上通过反射对方法的调用
这是委托的构造函数,有两个参数
public SayHelloDelegate(object target, string method) { }
委托类同时提供了两个只读属性Target和Method供使用,是将委托变量指向的对象和方法进行了包装,如果方法是静态方法,则Target为null,否则就指向对象的引用。Method属性返回一个System.Reflection.MethodInfo对象的引用。在我们调用Invoke方法时,其实是执行了委托变量指向的方法,只不过有一个内部包装和调用的机制。
补充:
经过一段时间的学习,在本博文的基础上写了一篇:
委托学习笔记后续:泛型委托及委托中所涉及到匿名方法、Lambda表达式
引用: