反射(Reflection)
在一个插件定义好之后,下一步要做的就是查看主程序是怎么加载插件的.为了实现这个目标,主程序使用了反射机制.反射是.NET中用于运行时查看类型信息的.在反射机制的帮助下,类型信息将被加载和查看.这样就可以通过检查这个类型以判断插件是否有效.如果类型通过了检查,那么插件就可以被添加到主程序的界面中,就可以被用户操作.
示例程序使用了.NET框架的三个内置类来使用反射:System.Reflection.Assembly,System.Type,和System.Activator.
System.Reflection.Assembly类描述了.NET的程序集.在.NET中,程序集是配置单元.对于一个典型的
Windows程序,程序集被配置为单一的Win32可执行文件,并且带有特定的附加信息,使之适应.NET运行环境.程序集也可以配置为Win32的DLL(动态链接库),同样需要带有.NET需要的附加信息.System.Reflection.Assembly类可以在运行的时候取得程序集的信息.这些信息包括程序集包含的类型信息.
System.Type类描述了类型定义.一个类型声明可以是一个类,接口,数组,结构体,或者枚举.在加载了一个类之后,System.Type类可以被用于枚举该类支持的方法,属性,事件和接口.
System.Activator类用于创建一个类的实例.
加载插件
列表四展示了LoadPlugs方法.LoadPlugs方法在HostForm.cs中定义,是HostForm类的一个private的非静态方法.LoadPlugs方法使用.NET的反射机制来加载可用的插件文件,并且验证它们是否符合被主程序使用的要求,然后把它们添加到主程序的树形显示区中.这个方法包含了下面几个步骤:
1.通过使用System.IO.Directory类,我们的代码可以用通配符来查找所有的以.plug为扩展名的文件.而Directory类的静态方法GetFiles能够返回一个System.String类型的数组,以得到每个符合要求的文件的物理路径.
2.在得到路径字符串数组之后,就可以开始把文件加载到System.Reflection.Assembly实例中了.建立Asdsembly对象的代码使用了try/catch代码块,这样如果某个文件并不是一个有效地.NET程序集,就会抛出异常,程序此时将弹出一个MessageBox对话框,告诉用户无法加载该文件.循环一直进行直到所有文件都已遍历完成.
3.在一个程序集加载之后,代码将遍历所有可访问到的类型信息,检查是否支持了HostCommon.IPlug接口.
4.如果所有类型都支持HostCommon.IPlug接口,那么代码继续验证这些类型,检查是否支持那些已预先为插件定义好的属性.如果没有支持,那么一个HostCommon.PlugNotValidException类型的异常将会被抛出,同样,主程序将会弹出一个MessageBox,告诉用户出错的具体信息.循环一直进行直到所有文件都已遍历完成.
5.最后,如果这些类型支持HostCommon.IPlug接口,也已定义了所有需要定义的属性,那么它将被包装为一个PlugTreeNode实例.这个实例就会被添加到主程序的树形显示区.
实现
主程序框架被设计为两个程序集.第一个程序集是Host.exe,它提供了主程序的
Windows窗体界面.第二个程序集是HostCommon.dll,它提供了主程序和插件之间进行通信所需的所有类型定义.比如,IPlug接口就是在HostCommon.dll里面配置的,这样它可以被主程序和插件等价的访问.这两个程序集在一个文件夹内,同样的,附加的作为插件的程序集也需要被配置在一起.那些程序集被配置在plugs文件夹内(主程序目录的一个子文件夹).EmployeePlug类在Employee.plug程序集中定义,而CustomerPlug类在Customer.plug程序集中定义.这个例子指定插件文件以.plug为扩展名.事实上这些插件就是个普通的.NET类库文件,只是通常库文件使用.dll扩展名,这里用.plug罢了.特殊的扩展名对于程序运行是完全没有影响的,但是它可以让用户更明确的知道这是个插件文件.
设计的比较
并不是一定要像例子程序这样设计才算正确的.比如,在开发一个带有插件的C#程序时,并不一定需要使用属性.例子里使用了两个自定义的属性,其实也可以新定义两个IPlug接口的参数来实现.这里选择用属性,是因为插件的名字和它的描述在本质上确实就是一个事物的属性,符合规范.当然了,使用属性会造成主程序需要更多的关于反射的代码.对于不同的需求,设计者总是需要做出合理的决定.
总结
示例程序被设计为尽量的简单,以帮助理解主程序和插件之间的通信.在实际做产品的时候,可以做很多的改进以满足实用要求.比如:
1.通过对IPlug接口增加更多的方法,属性,事件,可以增加主程序和插件之间的通信点.两者间的更多的交互操作使得插件可以做更多的事情.
2.可以允许用户主动选择需要加载的插件.
源代码
示例程序的完整的源代码可以在这里下载.
ftp://ftp.cuj.com/pub/2003/2101/walchesk.zip
图片一:

列表一:The IPlug interface
public interface IPlug { IPlugData[] GetData(); PlugDataEditControl GetEditControl(IPlugData Data); bool Save(string Path); bool Print(PrintDocument Document); } |
列表二:The PlugDisplayNameAttribute class definition
[AttributeUsage(AttributeTargets.Class)] public class PlugDisplayNameAttribute : System.Attribute { private string _displayName;
public PlugDisplayNameAttribute(string DisplayName) : base() { _displayName=DisplayName; return; }
public override string ToString() { return _displayName; } |
列表三:A partial listing of the EmployeePlug class definition
[PlugDisplayName("Employees")] [PlugDescription("This plug is for managing employee data")] public class EmployeePlug : System.Object, IPlug { public IPlugData[] GetData() { IPlugData[] data = new EmployeeData[] { new EmployeeData("Jerry", "Seinfeld") ,new EmployeeData("Bill", "Cosby") ,new EmployeeData("Martin", "Lawrence") };
return data; }
public PlugDataEditControl GetEditControl(IPlugData Data) { return new EmployeeControl((EmployeeData)Data); }
public bool Save(string Path) { //implementation not shown }
public bool Print(PrintDocument Document) { //implementation not shown } } |
列表四:The method LoadPlugs
private void LoadPlugs() { string[] files = Directory.GetFiles("Plugs", "*.plug");
foreach(string f in files) {
try { Assembly a = Assembly.LoadFrom(f); System.Type[] types = a.GetTypes(); foreach(System.Type type in types) { if(type.GetInterface("IPlug")!=null) { if(type.GetCustomAttributes(typeof(PlugDisplayNameAttribute), false).Length!=1) throw new PlugNotValidException(type, "PlugDisplayNameAttribute is not supported"); if(type.GetCustomAttributes(typeof(PlugDescriptionAttribute), false).Length!=1) throw new PlugNotValidException(type, "PlugDescriptionAttribute is not supported");
_tree.Nodes.Add(new PlugTreeNode(type)); } } } catch(Exception e) { MessageBox.Show(e.Message); } }
return; } |
[@more@]
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/79548/viewspace-926559/
,如需转载,请注明出处,否则将追究法律责任。