第六部分:委托事件
为什么有委托delegate
委托是一种数据类型,像类一样(可以声明委托类型变量)。方法参数可以是int、string、类类型
•voidM1(intn){ } √
•voidM2(string s){ } √
•voidM3(Person p){ } √
那么能不能把方法也作为参数进行传递?
•voidM4(M1 method){ method(); } ??能把方法作为参数吗??,如果能,那么把方法作为参数的意义何在?
•使用普通变量的时候是直接取值,或赋值。而使用委托变量的时候,我们还可以直接调用。
l
1.
委托带给我们的效果(好处)
l
2.
委托的语法
l
3.
委托的微观描述
l
4.
项目中什么时候用委托。
l
5.
委托的本质。
那么委托到底把方法存到哪里了?其实委托还是一个类。把方法包装成了一个委托。
方法是不能直接赋值的,那么能不能声明一个能存放方法的变量呢(委托)。
委托的使用
声明委托的方式:delegate 返回值类型 委托类型名(参数)
比如delegatevoid StringProcess(string s);
注意这里的除了前面的delegate,剩下部分和声明一个函数一样,但是StringProcess不是函数名,而是委托类型名
存储什么样的方法就声明什么类型(方法参数与返回值)的委托。
声明的委托是一种类型,就像int、Person一样,如果要用的话还要声明委托类型的变量,声明委托类型变量的方式:StringProcess f1;
将委托类型变量指向函数 StringProcess sp= new StringProcess(SayHello),这样就可以像调用普通函数一样把sp当成函数用了。委托可以看做是函数的指针。整数可以用整数变量指向它,对象可以用对象变量指向它,函数也可以用委托变量指向它。和直接调用函数的区别:用委托就可以指向任意的函数,哪怕是之前没定义的都可以,而不使用受限于那几种。
将委托类型变量指向函数还可以简化成StringProcess sp= SayHello,编译器帮我们进行了new。但是不能sp=PrintIt(),因为这样就成了函数调用。
public delegate intxxxDelegate(参数);
public delegate void xxxHandler();
class Person
{
}
Person p=….;
xxxHandlerdel=…;
委托使用案例:字符串数组处理
ldelegatestringProcessDelegate(strings);
static void ProcessIntArray(ProcessDelegatep)
{
string[] values = new string[]{ "aB","Cd"};
for (int i = 0; i <values.Length; i++)
{
values[i] = p(values[i]);
}
Console.WriteLine("处理后");
foreach (stringsin values)
{
Console.WriteLine(s);
}
}
编写函数,大写、小写、加引号
进一步体会:和直接调用函数的区别:用委托就可以指向任意的函数,哪怕是之前没定义的都可以,而不使用受限于那几种。
匿名方法(*,知道即可)
使用Delegate的时候很多时候没必要使用一个普通的方法,因为这个方法只有这个Delegate会用,并且只用一次,这时候使用匿名方法最合适。
匿名方法就是没有名字的方法。3就是没有名字的int对象。3+5就是两个匿名int对象的相加,允许匿名对象,就允许匿名方法。
ProcessWordDelegate p = delegate(string s)
{
Console.WriteLine(s);
};
知道C#中有匿名方法,看到这种写法知道是匿名函数即可。
匿名方法与lambda表达式最终编译为一个方法。
多播委托*(委托链,委托的组合)
delegatevoid ProcessWordDelegate(strings)
ProcessWordDelegate d =new ProcessWordDelegate(SayHello)+newProcessWordDelegate(ToLower)
多播委托如何处理返回值?
•委托绑定多个方法后,其中一个方法执行发生异常后面的方法还会继续执行吗?不会!
•一个重要的方法GetInvocationList();//返回一个Delegate[]类型。Delegate类是一个抽象类,是所有委托的父类。
组合的委托必须是同一个类型
相当于创建了一个按照组合的顺序依次调用的
新委托对象。
委托的组合一般是给事件用的,用普通的委托的时候很少用
class Program
{
static void Main(string[] args)
{
UpdateDelegate upde = M1;
upde += M2;
upde += M3;
upde -= M2;
upde = M4;
upde();
Console.ReadKey();
}
public static void M1()
{
Console.WriteLine("M1");
}
public static void M2()
{
Console.WriteLine("M2");
}
public static void M3()
{
Console.WriteLine("M3");
}
public static void M4()
{
Console.WriteLine("M4");
}
}
public delegate void UpdateDelegate();
==============================================
ResultDelegate M1 = T1;
M1 += T2;
M1 += T3;
M1 += T4;
int r = M1();
Console.WriteLine(r);
Console.ReadKey();
publicstatic int T1()
{
return 1;
}
public static int T2()
{
return 2;
}
public static int T3()
{
return 3;
}
public static int T4()
{
return 4;
}
public delegate int ResultDelegate();
==========================================
Delegate[]des= M1.GetInvocationList();
foreach (Delegate item in des)
{
ResultDelegate res = item as ResultDelegate;
Console.WriteLine(res());
}
==================================单独拿到里面每个方法
为委托的增减方法
ld+=SayHello,为委托增加一个方法,不要感觉奇怪,因为它就是d=d+ SayHello
ld-=SayHello,将方法从委托中移除。
lDelegate.Combine();
public delegate void MyDelegate<T>(T msg);
Action无返回值
Func有返回值
委托的不可变性
委托具有类似于string一样的不可变性。即时窗口中查看地址。
建议使用的时候尽量少定义自己的委托,使用系统中已经有的委托。减少程序集中定义的类型的个数.无参数,无返回值的委托可以用:Action。
委托的本质1(*)
其实就是一个类把方法包装了一下,委托都继承自System.MulticastDelegate,而System.MulticastDelegate又继承自System.Delegate
多播委托就是有一个委托数组,依次调用。
查看自己写的委托的IL代码:
看不到Invoke()方法的代码,该代码由CLR在运行时自动生成。参考备注1.
事件(通过委托实现的,委托才是事件能正常执行的核心内容)
事件语法:event ProcessWordDelegate 例子 OnInt
加了event关键字实现事件机制的好处:用了event事件,不可以修改事件已经注册的值;不可以冒充进行事件通知了。在IntUC类外部就不能通过OnInt(3)的方式调用注册的委托了。只能+=、-=!
课上练习:实现连续点击三次触发TriClick事件的按钮(用UserControl),用EventHandler这个委托就行。注意不要把判断次数的代码写到用控件的窗口上,否则就违反了封装的原则。
动态设置控件事件
事件本质论
event会自动生成一个private delegate变量和两个函数: add和remove,C#编译器用这两个方法支持+=和-=操作符 (*)。C#<>.Net。
publicevent MyDelegate OnEvent;
内部实现是(示例性)
privateMyDelegate OnEvent;
publicvoid Add(MyDelegate d)
{
OnEvent+=d;
}
publicvoid Remove(yDelegate d)
{
OnEvent-=d;
}
因为OnEvent是private的,所以在类外部不能OnEvent(1)触发事件,但是在类内部可以。
public的方法只有Add和Remove,所以只能+=、-=,其他的操作都是不可以的。
委托和事件的区别(常考)
委托和事件没有可比性,因为委托是数据类型,事件是对象(可以理解为对委托变量的封装。),下面说的是委托的对象(用委托方式实现的事件)和(标准的event方式实现)事件的区别。事件的内部是用委托实现的。(举例子:三种实现事件方式的区别(直接用委托实现、用私有委托+公有方法模拟事件,直接用event事件实现))
因为对于事件来讲,外部只能“注册自己+=、注销自己-=”,外界不可以注销其他的注册者,外界不可以主动触发事件,因此如果用Delegate就没法进行上面的控制,因此诞生了事件这种语法。add、remove。
事件是用来阉割委托实例的。事件只能add、remove自己,不能赋值。事件只能+=、-=,不能=、不能外部触发事件。
委托与事件总结
委托的作用:
•占位,在不知道将来要执行的方法的具体代码时,可以先用一个委托变量来代替方法调用(委托的返回值,参数列表要确定)。在实际调用之前,需要为委托赋值,否则为null。
事件的作用:
•事件的作用与委托变量一样,只是功能上比委托变量有更多的限制。(比如:1.只能通过+=或-=来绑定方法(事件处理程序)2.只能在类内部调用(触发)事件。)
在自定义控件(自己编写控件的时候,会大量用到.编写控件的时候,会写一些事件。但是当这些事件被触发以后,具体执行的那些事件处理程序是编写控件的人没法确定的。这个时候只能通过事件来占位(调用),具体调用的是哪个方法,由使用控件的人来决定(Click+=new委托(方法名);))