委托是面向对象的、类型安全的,是引用类型。使用delegate关键字进行定义。委托的本质就是一个类,继承自system.multicastdelegate,而它又派生自system.delegate。里面内置了几个方法 ,可以在类的外面声明委托,也可以在类的内部声明委托。
对委托的使用:先定义,后声明和实例化委托,然后作为参数传递给方法。
下面是几种委托定义的例子:
实例化委托时参数传递的是一个方法,方法的签名必须和委托的签名一样(即方法的返回值类型、参数列表的参数类型都必须和定义的委托一致)。
donothing()方法定义如下:
注意:委托的调用和直接执行方法的效果是一样的,例如:
在控制台的main()方法里面,结果如下:
从截图中能够看出:三种方式的输出高考志愿填报能填几个结果都是一样的。
委托类型:定义了委托实例可以调用的那类方法,具体来说,委托类型定义了方法的返回类型和参数类型,下面的代码定义了一个委托类型:
委托实例:把方法赋值给委托变量的时候就创建了委托实例,例如下面的代码:
也可以简写为下面的形式:
square是定义的一个方法,其方法定义如下:
委托的实例其实就是调用者的委托:调用者调用委托,然后委托调用目标方法,间接的把调用者和目标方法解耦合。
讲到这里可能有人会问:既然使用委托和直接调用方法的效果是一样的,那为什么还要使用委托呢,直接调用方法多么简单?下面先来看一个实际的例子。
先定义一个student类,里面有一些属性和方法,student类定义如下:
然后使用集合初始化器的方式初始化一个list<student>集合,填充一些测试数据:
现在有一个需求,找出list<student>集合里面年龄大于25的学生信息,代码如下:
使用一个foreach循环很容易得到全部年纪大于25的学生,这时又提出了需求:找出name长度大于2的学生、找出name长度大于2 而且年龄大于25 而且班级id是2的学生,代码如下:
观察上面的代码,你会发现里面有很多重复的代码:每次都要先准备一个查询结果集的集合,然后遍历数据源,判断条件,把满足条件的放入到集合中。可不可以把上面的代码进行优化呢?请看下面的代码:
在上面这段代码中,每次根据不同的type类型执行不同的判断条件,这样看起来可以把一些重复的代码进行了重用,但是这样又会有其他的问题:所有的判断逻辑都写在了一起,如果又增加了一种type类型或者判断逻辑改变了,就要修改整个代码,违反了开闭原则。
仔细观察上面的这段代码:getlist()方法需要传入一个int类型的参数故乡全文,根据不同的参数,执行对应的逻辑。那么可不可以直接传递逻辑进来呢?逻辑就是方法,也就是说能不能传递一个方法进来。可能有人会问题,方法都是进行调用啊,怎么能进行传递呢?答案是肯定的:那就是使用上面讲到的委托。
以查询年龄大于25的学生为例:逻辑就是判断学生的年龄是否大于25,返回一个bool值,如果大于25就添加到集合中,根据逻辑,可以得到下面的方法:
根据这个方法的签名可以定义如下的委托:
修改上面getlist的方法,把委托作为参数传递进来:
实例化委托:
另外两个可以定义如下的方法:
实例化委托如下:
观察getlistdelegate这个方法:保留了以前公用的代码:准备一个结果集的集合、循环遍历数据源,把符合条件的学生添加到结果集中,而判断逻辑放到了单独的一个方法中,如果判断逻辑改变了或者需要增加新的判断逻辑,只需要修改原有的判断逻辑或者新增判断逻辑即可,这样可以做到不需要修改getlistdelegate()这个方法,很好的符合开不原则。
可以总结出委托的一个应用:委托可以解除公用逻辑(准备结果集的集合、循环遍历数据源,添加到结果集中)和具体的业务逻辑(例如判断年龄大于25)的耦合,可以减少重复的代码。
委托实例化的时候不仅可以传入当前类型的普通方法,还可以传入静态、实例方法等,例如:
其中donothingstatic()方法定义如下:
student类的静态方法和实例方法定义如下:
总结:实例化委托时传入的方法只有一个要求:方法的签名和委托的签名一样,即返回值类型和大学英语四六级考试参数列表一致,无论该方法来自于当前类型的普通方法、静态方法或者其他类型的实例方法和静态方法。
链式委托也被称为“多播委托”,其本质是一个由多个委托组成的链表
。我们知道,所有的自定义委托都继承自system.multicastdelegate类,这个类就是为链式委托而设计的。当两个及以上的委托被链接到一个委托链时,调用头部的委托将导致该链上的所有委托方法都被执行。
像上面实例化委托的时候,一个委托类型的变量只能保存一个方法,使用多播委托,一个委托类型的变量可以保存多个方法,多播委托可以增加、减少委托,invoke的时候可以按顺序执行。
+= 为委托实例按顺序增加方法,形成方法链,invoke时,按顺序依次执行,例如下面的代码:
+=委托的最后输出结果是什么是?请看下面的截图:
可以看到,调用头部的委托导致了所有委托方法的执行。为委托+=增加方法让我们看起来像是委托被修改了,其实它们并没有被修改。事实上,委托是不变的。在给委托增加或移除方法时,实际发生的是创建了一个新的委托。
从上面的截图中可以看出:多播委托的执行结果是把所有传入的方法都执行一遍,而且是按照实例化时传入方法的顺序依次执行的。(上面传入了两次当前类型的donothing方法,所以会执行两边。)
-= 为委托实例移除方法,从方法链的尾部开始匹配,遇到第一个完全吻合的,移除且只移除一个,没有也不异常。
移除委托的执行结果是什么呢?在上面添加委托的时候传入了5个方法,移除委托的时候移除了4个方法,应该只会执行donothing()这一个方法,是这样的吗?看看下面的运行结果:
从截图中可以看出,最后的结果和我们猜测的结果不同,除了执行donothing()方法以外,还执行了study()方法,但是添加的时候我们只添加了一个study()方法,而且后面又移除掉了,那为什么还会执行这个方法呢?原因是因为添加和移除时候不是同一个实例的study()方法(添加和移除的时候都是new了一个新实例),所以移除的时候不会被移除掉。怎么证明上面的原因是否正确呢?请看下面的代码:
查看运行结果:
从运行结果中可以看出上面的原因是正确的。
注意:多播委托不能异步调用(即调用begininvoke()),因为多播委托里面有很多方法,异步调用的时候不知道该怎样执行,是把所有方法同步执行呢还是按照顺序依次执行呢,begininvoke不知道该如何调用,所以多播委托不能直接调用begininvoke()方法。那如果我想使用该怎么办呢?可以使用getinvocationlist()方法,f12查看getinvocationlist()方法的定义:
那么可以使用如下的代码:
上面的多播委托例子中一直都是使用的没有返回值的委托,如果是有返回值的委托,那么返回值是什么呢?请看下面的例子:
先定义几个有返回值的方法:
实例化委托:
运行程序查看结果:
从截图中可以看出:带返回值的多播委托的结果是最后添加的方法的返回值。中间方法的返回值都会被丢弃。
总结:多播委托一般用来调用无返回值的方法,不用来调用有返回值的方法,因为有返回值的多播委托中间的结果都会被丢弃掉。
我们可以对委托做如下的总结:
委托是不可变的。使用+=或-=操作符时,实际上是创建了新的委托实例,并把它赋给当前的委托变量。如果多播委托的返回类型不是void,那么调用者从最后一个被调用的方法来接收返回值,前面的方法仍然会被调用,但是其返回值就被弃用了。所有的委托类型都派生于system.multicastdelegate,而它又派生于system.delegate。c#会把作用于委托的+、-、+=、-=操作编译成使用system.delegate的combine和remove两个静态方法。事件是带e安徽中考成绩查询网vent关键字的委托的实例。
委托是一个类型,例如student类。
事件是委托类型的一个实例,例如具体的一个学生。
事件不能直接执行invoke()方法,可以限制变量被外部调用或者直接赋值。
注意:即使是在子类中,事件也不能调用invoke()方法。
来看下面的一个例子:
有一个cat类,里面有一个miao()的方法,猫叫了一声,然后触发一系列的后续动作,通常的实现代码如下:
调用miao()方法:
上面的代码可以实现上述的需求,但是这段代码耦合性很强,因为是在miao()方法里面直接调用别的实例的方法,以后无论是增加或者修改、调整方法的调用顺序,都要修改miao()方法,使得miao()方法不稳定。
下面使用委托来优化上面的代码:
调用:
上面的委托也可以改为事件实现:
调用:
到此这篇关于c#委托和事件的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持www.887551.com。绿色经济
本文发布于:2023-04-06 02:17:46,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/63dd14b922a878480e8a5edeeb7927dc.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:C#中的委托和事件.doc
本文 PDF 下载地址:C#中的委托和事件.pdf
留言与评论(共有 0 条评论) |