C#接口与事件编程.docx
- 文档编号:694906
- 上传时间:2023-04-29
- 格式:DOCX
- 页数:23
- 大小:23.58KB
C#接口与事件编程.docx
《C#接口与事件编程.docx》由会员分享,可在线阅读,更多相关《C#接口与事件编程.docx(23页珍藏版)》请在冰点文库上搜索。
C#接口与事件编程
在windows 编程中用到最多的就是控件的时间了,微软给我们很好的方式,把注意力放到事件执行方法的设计和编码上,但是但我们真正弄懂了事件的真正出发执行原理的话,对我们的编程的提高真是非常榜的,例如在windows编程中如果我单击了一个button按钮触发了button 的click事件 Button1_Click(){} ,但是有时候我们编程的时候,不但想要触发button 的单击事件,我还想要把其他的时间也要调用下来顺序执行,要实现这种方式,除了在方法最后对其他方法的调用,还可以利用将其他需要顺序执行的方法封装到button的click事件的委托对象中,这样就能够顺序执行毁掉方法列表中的程序了,而这种方式的实现是以清楚事件触发和委托的调用为前提的。
事件是类和对象向外界发出的消息,事件的执行是通过事件委托的方式,调用我们所准备好的处理方法,而是先消息的响应的。
要响应某些事件并针对某些事件执行我们意定的方法,需要做到以下几步:
1、声明事件委托。
2、声明事件。
3、添加事件的触发方法。
4、添加事件的处理程序(响应事件的方法)。
5、将指定的事件处理程序邦定到要处理的事件上(订阅事件)。
6、用户信息操作,并触发事件(调用事件的触发方法)。
7、通过事件委托的回调,执行我们需要的事件处理程序。
下面我们举一个简单的自定义事件处理程序的例子(控制台程序)
namespace事件
{
//发布事件的类
publicclassTestEventSource
{
//定义事件参数类
publicclassTestEventArgs:
EventArgs
{
publicreadonlycharKeyToRaiseEvent;
publicTestEventArgs(charkeyToRaiseEvent)
{
KeyToRaiseEvent=keyToRaiseEvent;
}
}
//定义delegate
publicdelegatevoidTestEventHandler(objectsender,TestEventArgse);
//用event关键字声明事件对象
publiceventTestEventHandlerTestEvent;
//事件触发方法
protectedvirtualvoidOnTestEvent(TestEventArgse)
{
if(TestEvent!
=null)
TestEvent(this,e);
}
//引发事件
publicvoidRaiseEvent(charkeyToRaiseEvent)
{
TestEventArgse=newTestEventArgs(keyToRaiseEvent);
OnTestEvent(e);
}
}
//监听事件的类
publicclassTestEventListener
{
//定义处理事件的方法,他与声明事件的delegate具有相同的参数和返回值类型
publicvoidKeyPressed(objectsender,TestEventSource.TestEventArgse)
{
Console.WriteLine("发送者:
{0},所按得健为:
{1}",sender,e.KeyToRaiseEvent);
}
//订阅事件
publicvoidSubscribe(TestEventSourceevenSource)
{
evenSource.TestEvent+=newTestEventSource.TestEventHandler(KeyPressed);
}
//取消订阅事件
publicvoidUnSubscribe(TestEventSourceevenSource)
{
evenSource.TestEvent-=newTestEventSource.TestEventHandler(KeyPressed);
}
}
//测试类
publicclassTest
{
publicstaticvoidMain()
{
//创建事件源对象
TestEventSourcees=newTestEventSource();
//创建监听对象
TestEventListenerel=newTestEventListener();
//订阅事件
Console.WriteLine("订阅事件\n");
el.Subscribe(es);
//引发事件
Console.WriteLine("输入一个字符,再按enter键");
strings=Console.ReadLine();
es.RaiseEvent(s.ToCharArray()[0]);
//取消订阅事件
Console.WriteLine("\n取消订阅事件\n");
el.UnSubscribe(es);
//引发事件
Console.WriteLine("输入一个字符,再按enter健");
s=Console.ReadLine();
es.RaiseEvent(s.ToCharArray()[0]);
}
}
}
程序执行结
订阅事件
输入一个字符,再按enter键
aaaa
发送者:
事件.TestEventSource,所按得健为:
a
取消订阅事件
输入一个字符,再按enter健
TestEventSource类。
他就相当于windows控件类一样,是事件的源,里面包含有事件的声明,以及存储调用参数的事件参数类,以及事件的触发方法。
TestEventListener类。
他提供了事件处理程序,并实现了事件处理程序和事件对象的邦定,当然时间处理程序可以放在别处,跟邦定程序(订阅事件)放在一起便于理解和调用
Test类,实例化自定义事件的事件源对象,并调用TestEventListener类中的Subscribe(es);方法进行事件对象和事件处理程序的邦定(订阅事件),调用TestEventSource类中的RaiseEvent(charkeyToRaiseEvent)引发对象,并有对象所指定的委托回调处理事件。
完成整个自定义事件。
其中 RaiseEvent(charkeyToRaiseEvent) 就相当于main()一样是自定义事件的执行入口, 从这个法开始---〉调用事件委托----〉查找订阅事件程序找到事件所封装的方法集----〉由委托回调事件处理程序并传递参数---〉执行事件处理程序。
基于DelegateEvent创建第一个IEvent对象
DelegateEvent与FunctionalReactiveProgramming,现在我们在它们两者之间架起一座桥梁。
也就是说,我们要从一个DelegateEvent
publicinterfaceIEvent
{
voidAdd(Action
}
publicclassDelegateEvent
{
publicIEvent
whereTEventArgs:
EventArgs
{
...
}
//othermembers...
}
比较可惜的是,即使得到了一个DelegateEvent
因为C#没有一个机制可以静态推断出一个委托的第二个参数是什么类型。
而且,事实上TDelegate并不是一个适合事件的委托类型。
这是因为,作为.NET中用于事件的委托,是需要满足一定要求的:
1.没有返回值(返回void)。
2.拥有两个参数。
3.第一个参数为object类型。
4.第二个参数为System.EventArgs的子类。
于是,我们现在的任务,便是基于目前的DelegateEvent,创建一个IEvent类型出来。
由于它是独立为DelegateEvent服务的,我将其定义为DelegateEvent的内部类:
publicclassDelegateEvent
{
publicIEvent
whereTEventArgs:
EventArgs
{
returnnewNativeEvent
}
//othermembers...
privateclassNativeEvent
IEvent
whereTEventArgs:
EventArgs
{
publicNativeEvent(DelegateEvent
{
...
}
//othermembers...
}
}
既然C#无法帮助我们获取TDelegate的第二个参数的类型,那么我们的第一步便是“提取”出这个Type对象:
privateclassNativeEvent
IEvent
whereTEventArgs:
EventArgs
{
privateDelegateEvent
privateTypem_eventArgsType;
publicNativeEvent(DelegateEvent
{
this.m_delegateEvent=delegateEvent;
this.m_eventArgsType=this.GetEventArgsType();
}
privateTypeGetEventArgsType()
{
varinvokeMethod=typeof(TDelegate).GetMethod("Invoke");
if(invokeMethod.ReturnType!
=typeof(void))
{
thrownewInvalidOperationException("Invaliddelegatetypeofevent.");
}
varparameters=invokeMethod.GetParameters();
if(parameters.Length!
=2||
parameters[0].ParameterType!
=typeof(object)||
!
typeof(TEventArgs).IsAssignableFrom(parameters[1].ParameterType))
{
thrownewInvalidOperationException("Invaliddelegatetypeofevent.");
}
returnparameters[1].ParameterType;
}
//othermembers...
}
在GetEventArgsType方法中,我们首先通过TDelegate的类型获取它Invoke方法——这便是委托的签名信息。
由于DelegateEvent已经帮我们确认TDelegate一定是一个委托,因此invokeMethod对象一定不为null。
于是我们开始全方位的检查,如果遇到了以下任意一种情况,则抛出异常:
1.返回类型不为void。
2.参数数量不为2。
3.第一个参数不为object类型。
4.第二个参数是否与TEventArgs兼容。
除了最后一点要求,其他都与之前的“标准”一一对应。
“标准”中的第4点已经由泛型参数TEventArgs的约束保证了,而我们检查的其实是TEventArgs与第二个参数类型的“兼容性”。
换句话说,我们的实现可以允许各种转换方式,如父子继承,隐式转换等等。
NativeEvent
因此,我们还要为NativeEvent添加这些代码:
privateclassNativeEvent
IEvent
whereTEventArgs:
EventArgs
{
privateList
publicvoidAdd(Action
{
if(this.m_callbacks.Count==0)
{
this.RegisterEventHandler();
}
this.m_callbacks.Add(callback);
}
privatevoidRegisterEventHandler()
{
...
}
privatevoidFireEvent(TEventArgsargs)
{
foreach(varcallbackinthis.m_callbacks)callback(args);
}
//othermembers...
}
当外界使用Add方法向NativeEvent添加回调方法时,会将回调方法保存起来。
需要注意的是,只有当外界“第一次”调用Add方法时,NatvieEvent才会去“监听”DelegateEvent对象,而监听这个行为是由RegisterEventHandler负责的。
它的实现是NativeEvent的又一关键。
既然是要监听DelegateEvent,自然是要准备一个委托对象并使用AddHandler方法交给DelegateEvent。
但是,我们现在只知道TDelegate类型,但是无法真正确定它的签名,因此,我们无法在编译期准备好一个方法来创建TDelegate对象,我们能做的就是在运行时动态生成一个委托。
那么这个委托的形式应该是什么样的呢?
这倒容易:
(sender,args)=>this.FireEvent((TEventArgs)args)
这是一个使用Lambda表达式生成的匿名方法,我们需要动态生成的委托便是这个模样。
于是剩下来的便交给表达式树吧:
privatestaticMethodInfos_fireEventMethod=
typeof(NativeEvent
BindingFlags.NonPublic|BindingFlags.Instance|BindingFlags.InvokeMethod);
privatevoidRegisterEventHandler()
{
//sender
varsenderExpr=Expression.Parameter(typeof(object),"sender");
//eventArgs
vareventArgsExpr=Expression.Parameter(this.m_eventArgsType,"args");
//(TEventArgs)eventArgs
varcastExpr=typeof(TEventArgs)==this.m_eventArgsType?
eventArgsExpr:
(Expression)Expression.Convert(eventArgsExpr,typeof(TEventArgs));
//this
varthisExpr=Expression.Constant(this);
//this.FireEvent((TEventArgs)args)
varbodyExpr=Expression.Call(thisExpr,s_fireEventMethod,castExpr);
//(sender,args)=>this.FireEvent((TEventArgs)args)
varlambdaExpr=Expression.Lambda
this.m_delegateEvent+=lambdaExpr.Compile();
}
构造表达式的过程分以下几步走:
1.构造一个object类型的参数sender。
2.构造一个委托第二个参数(m_eventArgsType)类型的参数args。
3.(如果TEventArgs与委托第二个参数类型不同,则)构造一个转化操作(TEventArgs)args。
4.构造一个常量this。
5.构造一个方法调用this.FireEvent((TEventArgs)args)。
6.将上述方法调用封装为一个Lambda表达式:
(sender,args)=>this.FireEvent((TEventArgs)args)。
Complie之后即大功告成。
可见,生成一个表达式树也是非常直观的,您在构造一个表达式时候,也可以使用这种方法,一步一步地进行下去。
在使用时,我们可以通过这样的代码:
varde=newDelegateEvent
vare=de.Wrap
e.Add((eventArgs)=>Console.WriteLine(eventArgs));
现在,您可以把它作为第一个IEvent对象,继续尝试FunctionalReactiveProgramming了。
C#浅谈事件监听及任务处理(监听属性值的改变及定时执行任务)
在很项目中都涉及到事件监听及任务处理的代码,例如:
想监听一个网络状态,就像QQ一样,一但网线一拨,它马上就弹出连接网络失败类似的东东,虽然我没深入了解QQ用的是什么原理,但下面的代码却可以实现一样的功能,各位如有更好的方法,欢迎讨论。
先上一段监听属性值的的代码
新建一个DoEvent.cs类
viewsourceprint?
01
usingSystem;
02
usingSystem.Web;
03
04
namespaceVvxT.Web
05
{
06
publicclassDoEvent
07
{
08
//需要监控的字段
09
privatestaticintcurrentState=-1;
10
11
///
12
///构造函数
13
///
14
publicDoEvent()
15
{
16
OnMyStateChanged+=newMyStateChanged(DoEvent_BeforeStateChanged);
17
}
18
19
//定义一个委托
20
privatedelegatevoidMyStateChanged(objectsender,EventArgse);
21
//定义一个委托关链的事件
22
privateeventMyStateChangedOnMyStateChanged;
23
24
//事件处理函数,属性值修改前的操作法
25
privatevoidDoEvent_BeforeStateChanged(objectsender,EventArgse)
26
{
27
//dosomething
28
}
29
30
//事件触发函数
31
privatevoidWhenMySta
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- C# 接口 事件 编程
![提示](https://static.bingdoc.com/images/bang_tan.gif)