C#之委托与事件
废话一堆:网上关于委托、事件的文章有很多,一千个哈姆雷特就有一千个莎士比亚,以下内容均是本人个人见解。
1.委托1.1委托的使用这一小章来学习一下怎么简单的使用委托,了解一些基本的知识。
这里先看一下其他所要用到的类的信息
///<summary>
///函数用例
///</summary>
publicclassManyMethodClass
{
publicManyMethodClass(){}
///<summary>
///实例函数
///</summary>
///<paramname="strmes"></param>
publicvoidInstanceMethod(stringstrmes)
{
Console.WriteLine("实例函数输出:"+strmes);
}
///<summary>
///静态函数
///</summary>
///<paramname="strmes"></param>
publicstaticvoidStaticMethod(stringstrmes)
{
Console.WriteLine("静态函数输出:"+strmes);
}
}
定义一个委托类型
publicdelegatevoidDisplayStringDelegate(stringstrmes);
实例化委托类型
//绑定实例函数第一种定义方式(实例函数)
DisplayStringDelegatedisstrdele_instance=newDisplayStringDelegate(newManyMethodClass().InstanceMethod);
//绑定静态函数第二种定义方式(静态函数)
DisplayStringDelegatedisstrdele_static=newDisplayStringDelegate(ManyMethodClass.StaticMethod);
//绑定委托实例第三种定义方式(委托实例)
DisplayStringDelegatedisstrdele_delegate=newDisplayStringDelegate(disstrdele_instance);
调用委托实例
disstrdele_instance.Invoke("HelloWord");
disstrdele_static("HelloWord");
disstrdele_delegate("HelloWord");
(ps:这里不同的调用方式效果都是一样的,在下一节会有讲到)
最后让我们看一下最终的效果图:
1.2委托的定义委托类型是类类型
///<summary>
///委托示例类
///</summary>
publicclassDelegateDemonStration
{
publicDelegateDemonStration(){}
publicdelegatevoidDisplayStringDelegate(stringstrmes);
}
在上面的示例代码中,我们定义了一个委托示例类DelegateDemonStration,在DelegateDemonStration的内部我们定义了上一节中讲到的委托的类型DisplayStringDelegate,
在这里先不谈DisplayStringDelegate它定义签名类型,先来谈谈它到底是什么样的存在。
我们来看一下代码:
///<summary>
///委托示例类
///</summary>
publicclassDelegateDemonStration
{
publicDelegateDemonStration(){}
publicclassDisplayStringDelegate:MulticastDelegate
{
publicDisplayStringDelegate(object@object,IntPtrmethod){}
publicvirtualIAsyncResultBeginInvoke(stringstrmes,AsyncCallbackcallback,object@object);
publicvirtualvoidEndInvoke(IAsyncResultresult);
publicvirtualvoidInvoke(stringstrmes);
}
}
当我们定义好一个委托类型的时候,在运行时C#编译器把【publicdelegatevoidDisplayStringDelegate(stringstrmes);】
编译为【publicclassDisplayStringDelegate:MulticastDelegate】
所以,各位客官只要记住在你定义一个委托类型的时候实际上你是定义了一个类
委托实例是函数指针
先让我们来看一下这一小节所要用到的示例代码(所用到的对象类型还是上一小节的内容):
DisplayStringDelegatedisstrdele=newDisplayStringDelegate(newManyMethodClass().InstanceMethod);
disstrdele+=ManyMethodClass.StaticMethod;//这里是委托类型推断绑定函数的一种方式几种方式可以自行百度
disstrdele+=newDisplayStringDelegate(ManyMethodClass.StaticMethod);
1.第一步在我们执行DisplayStringDelegatedisstrdele=newDisplayStringDelegate(newManyMethodClass().InstanceMethod);的时候,disstrdele实例是如下图一样:
2.第二步当我们继续执行代码disstrdele+=ManyMethodClass.StaticMethod;的时候,请再来看图:
这是disstrdele多指向了一个函数地址,而它的内部实现并不是外表看起来这样简单的:
这里简要的说一下执行第二步的时候
L_0012:是将第一步的disstrdele实例压入栈中
L_0013:将一个空引用压入栈中
L_0014:加载ManyMethodClass.StaticMethod函数地址
L_001a:实例化DisplayStringDelegate委托类型,假如它叫A
L_001f:将disstrdele和A合并
L_0024:将合并好的值转换成DisplayStringDelegate类型,并且存入L_0013中
L_0029:将L_0013赋值到disstrdele实例
(以上纯属个人理解,如有偏差请帮忙纠正,谢谢)
3.第三步当我们继续执行代码disstrdele+=newDisplayStringDelegate(ManyMethodClass.StaticMethod);的时候,过程和第二步相同:
终上所述,委托实例都会指向一个函数地址,当然喽合并过后新的委托实例宁当别论,所以委托实例是函数指针或者委托实例是函数指针列表
1.3委托实例的调用我们来看调用的代码:
disstrdele("HelloWord");
这里的内容是接着上一小节的内容继续讲的,拆分开来讲是为了让大家能看的更清楚。
看一下示意图吧,
从这里就可以看出来了,虽然disstrdele实例是合并后的委托实例,它的屁股上挂上了好多函数地址,但是执行这样函数的入口还只是一个,那就是Delegate.Invoke(string),
如果有人要问它的Invoke(string)内部是怎么实现的,暂时回答不了,因为具体的代码是动态生成的吧,不过我们可以自己猜想或者想象一下它是怎么实现的。
在这里就不多说了。
看了以上的内容能大概的知道或者分清一些概念性的东西。
这里本人只是讲了一把剑是由铁铸成的,可以用来切割、刺、劈、砍,至于这把剑怎么用就因人而异了。
2.事件2.1事件是什么?这一章我们来学习一下事件
照旧我们先来看一下示例代码:
publicclassEventDomeStration
{
publicEventDomeStration(){}
///<summary>
///定义好的一个示例
///</summary>
publiceventDisplayStringDelegateDisplayStringEventHandler;
}
在上面的代码中,我们定义了一个事件DisplayStringEventHandler,而在它名称前面的是一个DisplayStringDelegate委托类型(就是我们上一节所说的委托)。
就对于它而言来看一下MSIL,看看它究竟是什么样的:
.classpublicautoansibeforefieldinitEventDomeStrationextends[mscorlib]System.Object
{
.fieldprivateclassDelegateCaseAndEventCase.DisplayStringDelegateDisplayStringEventHandler
.eventDelegateCaseAndEventCase.DisplayStringDelegateDisplayStringEventHandler
{
.addoninstancevoidDelegateCaseAndEventCase.EventDomeStration::add_DisplayStringEventHandler(classDelegateCaseAndEventCase.DisplayStringDelegate)
.removeoninstancevoidDelegateCaseAndEventCase.EventDomeStration::remove_DisplayStringEventHandler(classDelegateCaseAndEventCase.DisplayStringDelegate)
}
}
从这里可以看到,定义好的一个事件就是一个私有(DisplayStringDelegate委托类型)的字段加上一个事件访问器,也就是相当于C#代码的这样:
publicclassEventDomeStration
{
publicEventDomeStration(){}
privateDisplayStringDelegatedisplaystringdele;
publiceventDisplayStringDelegateDisplaystringEventHandler
{
add
{
displaystringdele+=value;
}
remove
{
displaystringdele-=value;
}
}
}
在这里本人对事件的定义是:
事件就是所在对象里的属性,而属性的类型是委托类型,它是负责架设与外部对象即时通讯(传递消息)的桥梁,桥梁本身就是委托。
2.2事件的使用出门在外务工,难免要租房子住,每次找房子是件头疼的事请各位客官一起来看一下,就拿这个来举个例子。
先定义一个委托:publicdelegatevoid消息反馈(string反馈房子信息);
首先得有一个中介,暂且叫它为×××中介1,来看一下它的内部定义:
publicclass×××中介1
{
public×××中介1(){}
public×××中介1(string具体要求)
{
房源信息处理(具体要求);
}
publicevent消息反馈消息通知;
publicstring客户要求的信息_1=string.Empty;
privatevoid房源信息处理(string客户要求的信息)
{
//逻辑处理
//假如是有客户需要的则通知客户
if(消息通知!=null)
{
消息通知("有房子,什么样的,信息等等");
}
客户要求的信息_1=客户要求的信息;
//逻辑处理要是没有把信息移交给了中介2
×××中介2中介2=new×××中介2(this);
}
publicvoid其它中介消息通知(string房子信息)
{
if(消息通知!=null)
{
消息通知(房子信息);
}
}
}
中介有了,那现在我想要开始租房子怎么办?没关系,找中介。
首先先把“我”定义出来:
publicclass我
{
//比如我要租房子
public我(){}
publicvoid我要租房子(string具体的要求)
{
//找的是×××中介1
×××中介1中介1=new×××中介1(具体的要求);
中介1.消息通知+=new消息反馈(中介1_消息通知);
}
void中介1_消息通知(string房子信息)
{
//我可以从【房子信息】中知道是否有有合适的房子,如果有得话,是什么样的
}
}
publicclass×××中介2
{
publicstring客户要求信息=string.Empty;
public×××中介2(){}
public×××中介2(×××中介1中介1)
{
房源信息处理(中介1);
}
privatevoid房源信息处理(×××中介1中介1)
{
//逻辑处理
//判断是否有满足->中介1.客户要求的信息_1值条件的房源
//如果有
中介1.其它中介消息通知("房子有的,房子的信息等等");
}
}
上面定义了我、×××中介1、×××中介2,现在我要找房子了
我me=new我();
me.我要租房子("有床就行");
好了,“我”已经把信息发布出去了,现在就坐等×××中介1的通知了。
因为事件只能是对象本身内部触发,这个设计是合理,为什么不能用委托,委托也可以实现同样的功能啊?
假如用了委托,这个例子的意思就是“我”可以控制×××中介1有没有找到房子信息,这个是不符合常理的。
因为“我”是找的×××中介1,×××中介1根据“我”的要求没找到,把这个信息交给了×××中介2,让×××中介2帮忙找找,然后中×××中介2找到了,
但是中×××中介2并不能直接通知“我”,如果可以直接通知的话,这样“我”会告×××中介1侵犯客户隐私,
所以只能是×××中介2告诉×××中介1找到了【也就是代码:×××中介1.其它中介消息通知("房子有的,房子的信息等等");】,然后再由×××中介1来通知“我”。
作者:金源
出处:http://jinyuan.blog.51cto.com/
本文版权归作者和51CTO共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。