作业:邮箱用户名替换成星号
复习:
什么情况用委托?委托有什么用?
当一个类型中需要嵌入一段代码,但这段代码具有不确定性
自定义控件的事件会大量使用委托。
可以把静态方法和私有方法赋值给委托变量,只要能使用到委托变量的地方就能使用该方法。打破了访问修饰符的限制。
一、 多播委托
多播委托中方法调用的顺序与增加方法的顺序一致。但不要依赖于这个顺序。
通过遍历,调用委托的每个方法。通过调用GetInvocationList()方法,返回当前委托的所有方法。返回值类型是Delegate数组。
多播委托内部是将绑定在当前委托对象上的方法都转换为一个委托对象,且存储在一个叫_invocationList的object数组中。调用委托时,就是循环遍历_invocationList数组,并调用其中的每一个委托。
当多播委托中一个方法执行时发生了异常,则后续方法不再执行。
如果直接使用等号赋值,会将前面绑定的所有其他方法都覆盖掉。
多播委托中也只能存储同一类型的委托。
如果委托有返回值并且在调用列表中有一个以上的方法(多播委托),会发生下面的情况:
1、调用列表中最后一个方法返回的值就是委托调用返回的值。
2、调用列表中所有其他方法的返回值都会被忽略。
(一)委托的不可变性
委托有类似于string的不可变性。
建议使用的时候尽量少定义自己的委托,使用系统中已有的委托。
(二)委托类(System.MulticastDelegate)的三个重要成员
(三)委托的应用:
多线程
自定义类
窗体间回传值
二、 事件
(一) 事件语法:event ProcessWordDelegate
加了event关键字实现事件机制的好处:用了event事件,不可以修改事件已经注册的值;不可以冒充进行事件通知。
由于委托可以用“=”赋值,可能将前面已经注册的程序都覆盖掉。
使用委托与使用事件的区别就是一个event关键字
事件只能通过+=或-=赋值,避免了事件覆盖的问题。
事件不能在定义事件的外部触发,避免了冒充事件触发的问题。
通过观察生成的代码可以发现,事件最终生成了:
1、 一个私有的委托;
2、 两个public的方法Add和Remove,分别用来实现事件的+=和-=
(二)有关事件的重要事项
1、触发(raise)事件:调用(invoke)或触发(fire)事件的术语。当事件被触发时,所有注册到它的方法都会被依次调用。
2、发布者(publisher):让事件被其他类或结构可见并使用的类或结构。除了事件,发布者还包含了触发事件的代码。
3、订阅者(subscriber):把事件和发布者关联注册的类或结构。
4、事件处理程序(event handler):注册到事件的方法。可以在事件所在的类或结构中,或者在不同的类或结构中。
(三)源代码组件概览
需要在事件中使用的代码有五部分:(结合《C#图解教程》的定时器案例理解)
委托类型声明:事件和事件处理程序必须有共同的签名和返回类型,它们通过委托类型声明进行描述。我们通常用EventHander类,由系统声明。
事件处理程序声明:这些在订阅者类的方法(事件处理程序)中的描述会在事件触发时被执行。它们不需要有独立的方法,它们可以是匿名方法或lambda表达式。例中,订阅者ClassA的TimerHanderA方法。
事件声明:这个事件发布者类中的声明保存并调用事件处理程序。例中,发布者MyTimerClass类:public event EventHandler Elapsed;
事件注册:这段代码把事件连接到事件处理程序。通常在Main方法中完成。例中,mc.Elapsed+= ca.TimerHanderA;
触发事件的代码:发布者类中的这段代码调用事件导致它调用事件处理程序。例中,MyTimerClass类:
private void OnOneSecond(objectobj,EventArgs e)
{
if(Elapsed!=null)
{
Elapsed(obj,e);
}
}
三、 程序集
程序集是.net中的概念。程序集(Assembly),可以看做是一堆相关类打一个包,相当于java中的jar包。
程序集可以包含资源文件、IL代码、元数据(描述自己的信息)。元数据分为:类型元素据(描述在代码中定义的类型和成员)和程序集元数据(程序集清单、版本号、名称等)
微软的程序集(全局程序集缓存)默认安装在C:\windows\Assembly文件夹下
跨平台调用(P/Invok):用.Net调用C语言程序
四、 反射
(一)加载C:\TestLib.dll程序集
Assembly asm = Assembly.LoadFile()
获取SayHi方法
MethodInfo method = typeClass1.GetMethod(“SayHi”);
调用该方法:
method.Invoke
(二)写一个可以扩展插件的主程序
步骤:
1、主程序窗体加载时查找dll文件
2、循环遍历每个dll文件,通过Assembly.LoadFile加载每个dll文件
3、通过Assembly.GetExportedTypes()获取程序集中的所有public类型的成员
4、判断每个类型是否实现了Ieditor接口,并且是非抽象的。判断时用IsAssignableFrom(确定当前Type的实例是否可以从指定Type的实例分配)
5、通过Activator.CreatInstance(type)创建这个类型的对象,然后赋值给IEditor接口,这时就可以通过editor.PluginName获取插件名,添加到菜单栏上
6、为刚刚添加的菜单项注册一个Click事件,在Click事件中调用插件类的“启动插件方法”,这样就可以启动插件了。
(三)写插件
步骤:
1、 添加类库,添加对接口类库的引用(注意,插件都是类库,不要添加成了应用程序)
2、 插件类实现主程序接口
3、 具体重写接口的属性和方法
举例:TextEditor程序加SetFont插件
1、 添加SetFontPluginClassLibrary类库,在类库中添加对InterfaceClassLibrary的引用
2、 将Class1.cs改名为SetFontClass.cs,并继承InterfaceClassLibrary下的Ieditor接口
3、 重写PluginName属性,命名为“字体”;
重写StartProgram方法:需要新建一个SetFontForm的WinForm类的实例,实现修改字体的窗体。
4、添加一个WinForm的SetFontForm.cs类,在这个类中修改StartProgram方法传来的txtBox中的字体,所有要重载一个public SetFontForm(TextBoxtxtBox):this()的构造函数,将txtBox的内容传入
5、通过
float fontSize = float.Parse(cboFontSize.Text);
this.txtBox.Font = newFont (cboFontFamily.Text, fontSize);
修改字体和字号