null第七讲第七讲委托和事件委托*委托委托是一种类型,即它与class , interface , struct ,enum处于同一级别,而且它是引用类型.
任何委托类型都是system.delegate类的派生类,但不允许从system.delegate类显式派生新类.
可用delegate关键词定义一个委托类型
委托类型用于封装对方法的调用委托*委托定义委托类型[定义委托将要调用的方法的签名]
public delegate 返回值类型 类型名称(形参列表);null*实例化委托类型,得到委托对象[确定要调用方法的对象及方法的名称]
委托类型 委托对象 = new 委托类型(对象名.方法名);
调用某类的实例方法
委托类型 委托对象 = new 委托类型(类型名.方法名);
调用某类的静态方法null*调用委托,实现对方法的调用.[给委托对象传参数,得到委托对象的返回值]
委托对象(实参);好处在哪里*好处在哪里调方法的人不必知道方法属于哪个类,方法叫什么名字.
用在哪里?
事件与事件处理程序的关联事件*事件一件事情
对象发生了一件事情或对象的状态发生了变化
人生病了.
按钮被用户点击了.
窗口被关掉了
一个形态的颜色从红色变成蓝色
事件是类对类的使用者发出的一个通知
事件发生后可能要进行一些处理
事件处理程序[方法]
事件与事件处理程序如何关联呢?委托事件*事件事件是类型的成员,本身不是类型
事件的类型是事件定义好的某种委托类型
事件发生时,委托对象会调用相关联的方法,即事件的处理程序.事件*事件在引发事件的类中声明事件[将事件声明为某种委托类型]
public event 某委托类型 事件名称
订阅事件:在引发事件的类的使用者[另一个类]给事件赋值,值为委托类型的实例,即一个委托对象,委托实例化时已决定要调哪个方法
引发事件:在引发事件的类中调用委托控件事件及其委托*控件事件及其委托Click , Enter , KeyPress …
EventHandler委托*委托Multiply(int,int)
{
….
}
Divide(int,int)
{
….
}
可以引用任何方法,将在运行时决定委托和方法必须具有相同的签名---
public delegate Call(int num1, int num2);
---定义委托 2-1 *演示:示例 3定义委托 2-1 [访问修饰符] delegate 返回类型 委托名(); 语法class Delegates
{
public delegate int Call(int num1, int num2);
class Math
{
public int Multiply(int num1, int num2)
{
// 实现
}
public int Divide(int num1, int num2)
{// 实现
}
}
class TestDelegates
{
static void Main()
{
Call objCall;
Math objMath = new Math();
objCall = new Call(math.Multiply);
}
}将方法与委托关联起来定义委托 2-2*定义委托 2-2class Delegates
{
// 委托定义
public delegate int Call(int num1, int num2);
class Math
{
// 乘法方法
public int Multiply(int num1, int num2)
{
return num1*num2;
}
// 除法方法
public int Divide(int num1, int num2)
{
return num1/num2;
}
}static void Main(string[] args)
{
// 委托的对象
Call objCall;
// Math 类的对象
Math objMath = new Math();
// 将方法与委托关联起来
objCall = new Call(objMath.Multiply);
// 将委托实例化
result = objCall(5, 3);
System.Console.WriteLine("结果为 {0}", result);
} 将方法与委托关联起来事件*事件“请听题~”集中注意力聆听其他人事件源事件的发布者事件的订阅人未订阅该事件 定义事件
为对象订阅该事件
将发生的事件通知给订阅人定义事件*定义事件[访问修饰符] event 委托名 事件名; 语法public delegate void delegateMe();
private event delegateMe eventMe;订阅事件 *订阅事件 eventMe += new delegateMe(objA.Method);
eventMe += new delegateMe(objB.Method);通知订阅对象 *通知订阅对象 if(condition)
{
eventMe();
}调用订阅特定事件的对象的所有委托示例*演示:示例 4示例class TestEvents
{
[STAThread]
static void Main(string[] args)
{
// 委托的对象
Delegate objDelegate = new Delegate();
// ClassA 的对象
ClassA objClassA = new ClassA();
// ClassB 的对象
ClassB objClassB = new ClassB();
// 订阅该事件
objDelegate.NotifyEveryOne +=
new Delegate.MeDelegate(objClassA.DispMethod);
objDelegate.NotifyEveryOne +=
new Delegate.MeDelegate(objClassB.DispMethod);
objDelegate.Notify();
}
}示例*示例class Delegate
{
// 定义委托
public delegate void MeDelegate();
// 定义事件
public event MeDelegate NotifyEveryOne;
public void Notify()
{
// 如果事件不为 null
if(NotifyEveryOne != null)
{
Console.WriteLine("触发事件:");
// 触发事件
NotifyEveryOne();
}
}
}示例*示例class ClassA
{
public void DispMethod()
{
Console.WriteLine(“Class A 已接到
NotifyEveryOne 事件的通知!");
}
}
// 第二个类
class ClassB
{
public void DispMethod()
{
Console.WriteLine(“Class B 已接到
NotifyEveryOne 事件的通知! ");
}
}
System.EventArgs类System.EventArgs类System.EventArgs类就是我们常说的事件参数类
public class NumberReachedEventArgs : EventArgs
{
private int _reached;
public NumberReachedEventArgs(int num)
{
this._reached = num;
}
public int ReachedNumber
{
get
{
return _reached;
}
} } *Net Framework中的委托与事件
Net Framework中的委托与事件
.Net Framework的编码规范:
1.委托类型的名称都应该以EventHandler结束。
2.委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个 EventArgs类型(或继承自EventArgs)。
3.事件的命名为 委托去掉 EventHandler之后剩余的部分。
4.继承自EventArgs的类型应该以EventArgs结尾。
再做一下说明:
1. 委托声明原型中的Object类型的参数代表了Subject,也就是监视对象。
2. EventArgs 对象包含了Observer所感兴趣的数据。*为什么要使用事件而不是委托变量?
为什么要使用事件而不是委托变量?
1.主要是从封装性和易用性上去考虑
2.事件应该由事件发布者触发,而不应该由客户端(客户程序)来触发。
NOTE:注 意这里术语的变化,当我们单独谈论事件,我们说发布者(publisher)、订阅者(subscriber)、客户端(client)。当我们讨论 Observer模式,我们说主题(subject)和观察者(observer)。客户端通常是包含Main()方法的Program类。
class Program { static void Main(string[] args) { Publishser pub = new Publishser(); Subscriber sub = new Subscriber(); pub.NumberChanged += new NumberChangedEventHandler(sub.OnNumberChanged); pub.DoSomething(); // 应该通过DoSomething()来触发事件 pub.NumberChanged(100); // 但可以被这样直接调用,对委托变量的不恰当使用 } } // 定义委托 public delegate void NumberChangedEventHandler(int count);
*null// 定义事件发布者
public class Publishser { private int count; public NumberChangedEventHandler NumberChanged; // 声明委托变量 //public event NumberChangedEventHandler NumberChanged; // 声明一个事件 public void DoSomething() { // 在这里完成一些工作 ... if (NumberChanged != null) { // 触发事件 count++; NumberChanged(count); } } } // 定义事件订阅者 public class Subscriber { public void OnNumberChanged(int count) { Console.WriteLine("Subscriber notified: count = {0}", count); } }*null上 面代码定义了一个NumberChangedEventHandler委托,然后我们创建了事件的发布者Publisher和订阅者 Subscriber。当使用委托变量时,客户端可以直接通过委托变量触发事件,也就是直接调用pub.NumberChanged(100),这将会影 响到所有注册了该委托的订阅者。而事件的本意应该为在事件发布者在其本身的某个行为中触发,比如说在方法DoSomething()中满足某个条件后触发。通过添加event关键字来发布事件,事件发布者的封装性会更好,事 件仅仅是供其他类型订阅,而客户端不能直接触发事件(语句pub.NumberChanged(100)无法通过编译),事件只能在事件发布者 Publisher类的内部触发(比如在方法pub.DoSomething()中),换言之,就是NumberChanged(100)语句只能在 Publisher内部被调用。
尝试一下,将委托变量的声明那行代码注释掉,然后取消下面事件声明的注释。此时程序是无法 编译的,当你使用了event关键字之后,直接在客户端触发事件这种行为,也就是直接调用pub.NumberChanged(100),是被禁止的。事 件只能通过调用DoSomething()来触发。这样才是事件的本意,事件发布者的封装才会更好。
就好像如果我们要定义一个数字类型,我 们会使用int而不是使用object一样,给予对象过多的能力并不见得是一件好事,应该是越合适越好。尽管直接使用委托变量通常不会有什么问题,但它给 了客户端不应具有的能力,而使用事件,可以限制这一能力,更精确地对类型进行封装。
*为什么委托定义的返回值通常都为void?
为什么委托定义的返回值通常都为void?
因为委托变量可以供多个订阅者注册,如果定义了返回值,那么多个订阅者的方法 都会向发布者返回数值,结果就是后面一个返回的方法值将前面的返回值覆盖掉了,因此,实际上只能获得最后一个方法调用的返回值。除此以外,发布者和订阅者是松耦合的,发布者根本不关心谁订阅了它的事件、为什么要订阅,更别说订阅者的返回值了,所以返回订阅者的方法返回值大多数 情况下根本没有必要。
class Program { static void Main(string[] args) { Publishser pub = new Publishser(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.NumberChanged += new GeneralEventHandler(sub1.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(sub2.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(sub3.OnNumberChanged); pub.DoSomething(); // 触发事件 } } // 定义委托 public delegate string GeneralEventHandler();
*null// 定义事件发布者 public class Publishser { public event GeneralEventHandler NumberChanged; // 声明一个事件 public void DoSomething() { if (NumberChanged != null) { // 触发事件 string rtn = NumberChanged(); Console.WriteLine(rtn); // 打印返回的字符串,输出为Subscriber3 } } } // 定义事件订阅者 public class Subscriber1 { public string OnNumberChanged() { return "Subscriber1"; } } public class Subscriber2 { /* 略,与上类似,返回Subscriber2*/ } public class Subscriber3 { /* 略,与上类似,返回Subscriber3*/ }*如何让事件只允许一个客户订阅?
如何让事件只允许一个客户订阅?
// 定义事件发布者 public class Publishser { private event GeneralEventHandler NumberChanged; // 声明一个私有事件 // 注册事件 public void Register(GeneralEventHandler method) { NumberChanged = method; } // 取消注册 public void UnRegister(GeneralEventHandler method) { NumberChanged -= method; } public void DoSomething() { // 做某些其余的事情 if (NumberChanged != null) { // 触发事件 string rtn = NumberChanged(); Console.WriteLine("Return: {0}", rtn); // 打印返回的字符串,输出为Subscriber3 } } }
NOTE:注意上面,在UnRegister()中,没有进行任何判断就使用了NumberChanged-=method语句。这是因为即使method方法没有进行过注册,此行语句也不会有任何问题,不会抛出异常,仅仅是不会产生任何效果而已。注意在Register()方法中,我们使用了赋值操作符“=”,而非“+=”,通过这种方式就避免了多个方法注册。
*事件访问器(Event Accessor)事件访问器(Event Accessor)class Program { static void Main(string[] args) { Publishser pub = new Publishser(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); pub.NumberChanged -= sub1.OnNumberChanged; // 不会有任何反应 pub.NumberChanged += sub2.OnNumberChanged; // 注册了sub2 pub.NumberChanged += sub1.OnNumberChanged; // sub1将sub2的覆盖掉了 pub.DoSomething(); // 触发事件 } } // 定义委托 public delegate string GeneralEventHandler(); // 定义事件发布者 public class Publishser { // 声明一个委托变量 private GeneralEventHandler numberChanged; // 事件访问器的定义 public event GeneralEventHandler NumberChanged { add { numberChanged = value; } remove { numberChanged -= value; } }
*null public void DoSomething() { // 做某些其他的事情 if (numberChanged != null) { // 通过委托变量触发事件 string rtn = numberChanged(); Console.WriteLine("Return: {0}", rtn); // 打印返回的字符串 } } } // 定义事件订阅者 public class Subscriber1 { public string OnNumberChanged() { Console.WriteLine("Subscriber1 Invoked!"); return "Subscriber1"; } } public class Subscriber2 {/* 与上类同,略 */} public class Subscriber3 {/* 与上类同,略 */}
上面代码中类似属性的public event GeneralEventHandler NumberChanged {add{...}remove{...}}语句便是事件访问器。使用了事件访问器以后,在DoSomething方法中便只能通过 numberChanged委托变量来触发事件,而不能NumberChanged事件访问器(注意它们的大小写不同)触发,它只用于注册和取消注册。下面是代码输出: Subscriber1 Invoked! Return: Subscriber1
*获得多个返回值与异常处理
获得多个返回值与异常处理
委托定义在编译时会生成一个继承自 MulticastDelegate的类,而这个MulticastDelegate又继承自Delegate,在Delegate内部,维护了一个委托 链表,链表上的每一个元素,为一个只包含一个目标方法的委托对象。而通过Delegate基类的GetInvocationList()静态方法,可以获 得这个委托链表。随后我们遍历这个链表,通过链表中的每个委托对象来调用方法,这样就可以分别获得每个方法的返回值。
class Program4 { static void Main(string[] args) { Publishser pub = new Publishser(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.NumberChanged += new DemoEventHandler(sub1.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub2.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub3.OnNumberChanged); List list = pub.DoSomething(); //调用方法,在方法内触发事件 foreach (string str in list) { Console.WriteLine(str); } } }
*nullpublic delegate string DemoEventHandler(int num); // 定义事件发布者 public class Publishser { public event DemoEventHandler NumberChanged; // 声明一个事件 public List DoSomething() { // 做某些其他的事 List strList = new List(); if (NumberChanged == null) return strList; // 获得委托数组 Delegate[] delArray = NumberChanged.GetInvocationList(); foreach (Delegate del in delArray) { // 进行一个向下转换 DemoEventHandler method = (DemoEventHandler)del; strList.Add(method(100)); // 调用方法并获取返回值 } return strList; } } // 定义事件订阅者 public class Subscriber1 { public string OnNumberChanged(int num) { Console.WriteLine("Subscriber1 invoked, number:{0}", num); return "[Subscriber1 returned]"; } } public class Subscriber3 {与上面类同,略} public class Subscriber3 {与上面类同,略}
输出结果:
Subscriber1 invoked, number:100 Subscriber2 invoked, number:100 Subscriber3 invoked, number:100 [Subscriber1 returned] [Subscriber2 returned] [Subscriber3 returned]
*null异常处理
class Program5 { static void Main(string[] args) { Publisher pub = new Publisher(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.NumberChanged += new DemoEventHandler(sub1.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub2.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub3.OnNumberChanged); } } public class Publisher { public event EventHandler MyEvent; public void DoSomething() { // 做某些其他的事情 if (MyEvent != null) { try { MyEvent(this, EventArgs.Empty); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.Message); } } } }
*nullpublic class Subscriber1 { public void OnEvent(object sender, EventArgs e) { Console.WriteLine("Subscriber1 Invoked!"); } } public class Subscriber2 { public void OnEvent(object sender, EventArgs e) { throw new Exception("Subscriber2 Failed"); } } public class Subscriber3 {/* 与Subsciber1类同,略*/}
结果是:Subscriber1 Invoked! Exception: Subscriber2 Failed
可以看到,尽管捕获了异常,使得程序没有异常结束,但是却影响到了后面的订阅者,因为Subscriber3也订阅了事件,但是却没有收到事件通知(它的方法没有被调用)。此时,可以先获得委托链表,然后在遍历链表的循环中处理异常。修改一下DoSomething方法:
public void DoSomething() { if (MyEvent != null) { Delegate[] delArray = MyEvent.GetInvocationList(); foreach (Delegate del in delArray) { EventHandler method = (EventHandler)del; // 强制转换为具体的委托类型 try { method(this, EventArgs.Empty); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.Message) } } }}
*DynamicInvoke()方法DynamicInvoke()方法public object DynamicInvoke(params object[] args);
这可能是调用委托最通用的方法了,适用于所有类型的委托。它接受的参数为object[],也就是说它可以将任意数量的任意类型作为参数,并返回单个object对象。上面的DoSomething()方法也可以改写成下面这种通用形式:
public void DoSomething() { // 做某些其他的事情 if (MyEvent != null) { Delegate[] delArray = MyEvent.GetInvocationList(); foreach (Delegate del in delArray) { try { // 使用DynamicInvoke方法触发事件 del.DynamicInvoke(this, EventArgs.Empty); } catch (Exception e) { Console.WriteLine("Exception: {0}", e.Message); } } } }
*null注 意现在在DoSomething()方法中,取消了向具体委托类型的向下转换,现在没有了任何的基于特定委托类型的代码,而 DynamicInvoke又可以接受任何类型的参数,且返回一个object对象。所以完全可以将DoSomething()方法抽象出来,使它成 为一个公共方法,然后供其他类来调用。
// 触发某个事件,以列表形式返回所有方法的返回值 public static object[] FireEvent(Delegate del, params object[] args){ List
本文档为【C#委托与事件】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。